diff --git a/.ansible-freeipa.metadata b/.ansible-freeipa.metadata index d534268..c58e727 100644 --- a/.ansible-freeipa.metadata +++ b/.ansible-freeipa.metadata @@ -1 +1 @@ -583ac570c030eb68a2026a506054f2f93587beb4 SOURCES/ansible-freeipa-0.1.8.tar.gz +5d09d3b590e8568d04edb288c9c515e308f3168f SOURCES/ansible-freeipa-0.1.12.tar.gz diff --git a/.gitignore b/.gitignore index b19cd6f..7591236 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/ansible-freeipa-0.1.8.tar.gz +SOURCES/ansible-freeipa-0.1.12.tar.gz diff --git a/SOURCES/ansible-freeipa-0.1.12-Add-support-for-option-name_from_ip-in-ipadnszone-mo_rhbz#1845056.patch b/SOURCES/ansible-freeipa-0.1.12-Add-support-for-option-name_from_ip-in-ipadnszone-mo_rhbz#1845056.patch new file mode 100644 index 0000000..4adea24 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Add-support-for-option-name_from_ip-in-ipadnszone-mo_rhbz#1845056.patch @@ -0,0 +1,556 @@ +From abbd15e6f50718119b4dd0380913d2d646eb7638 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Mon, 3 Aug 2020 19:23:07 -0300 +Subject: [PATCH] Add support for option `name_from_ip` in ipadnszone module. + +IPA CLI has an option `name_from_ip` that provide a name for a zone +from the reverse IP address, so that it can be used to, for example, +manage PTR DNS records. + +This patch adds a similar attribute to ipadnszone module, where it +will try to find the proper zone name, using DNS resolve, or provide +a sane default, if a the zone name cannot be resolved. + +The option `name_from_ip` must be used instead of `name` in playbooks, +and it is a string, and not a list. + +A new example playbook was added: + + playbooks/dnszone/dnszone-reverse-from-ip.yml + +A new test playbook was added: + + tests/dnszone/test_dnszone_name_from_ip.yml +--- + README-dnszone.md | 3 +- + playbooks/dnszone/dnszone-reverse-from-ip.yml | 10 ++ + plugins/modules/ipadnszone.py | 65 +++++++++- + tests/dnszone/test_dnszone_name_from_ip.yml | 112 ++++++++++++++++++ + 4 files changed, 186 insertions(+), 4 deletions(-) + create mode 100644 playbooks/dnszone/dnszone-reverse-from-ip.yml + create mode 100644 tests/dnszone/test_dnszone_name_from_ip.yml + +diff --git a/README-dnszone.md b/README-dnszone.md +index 9c9b12c..48b019a 100644 +--- a/README-dnszone.md ++++ b/README-dnszone.md +@@ -163,7 +163,8 @@ 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 +-`name` \| `zone_name` | The zone name string or list of strings. | yes ++`name` \| `zone_name` | The zone name string or list of strings. | no ++`name_from_ip` | Derive zone name from reverse of IP (PTR). | no + `forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no +   | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes +   | `port` - The custom port that should be used on this server. | no +diff --git a/playbooks/dnszone/dnszone-reverse-from-ip.yml b/playbooks/dnszone/dnszone-reverse-from-ip.yml +new file mode 100644 +index 0000000..5693872 +--- /dev/null ++++ b/playbooks/dnszone/dnszone-reverse-from-ip.yml +@@ -0,0 +1,10 @@ ++--- ++- name: Playbook to ensure DNS zone exist ++ hosts: ipaserver ++ become: true ++ ++ tasks: ++ - name: Ensure zone exist, finding zone name from IP address. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 10.1.2.3 +diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py +index c5e812a..901bfef 100644 +--- a/plugins/modules/ipadnszone.py ++++ b/plugins/modules/ipadnszone.py +@@ -43,6 +43,10 @@ options: + required: true + type: list + alises: ["zone_name"] ++ name_from_ip: ++ description: Derive zone name from reverse of IP (PTR). ++ required: false ++ type: str + forwarders: + description: The list of global DNS forwarders. + required: false +@@ -197,6 +201,12 @@ from ansible.module_utils.ansible_freeipa_module import ( + is_ipv6_addr, + is_valid_port, + ) # noqa: E402 ++import netaddr ++import six ++ ++ ++if six.PY3: ++ unicode = str + + + class DNSZoneModule(FreeIPABaseModule): +@@ -354,6 +364,31 @@ class DNSZoneModule(FreeIPABaseModule): + if not zone and self.ipa_params.skip_nameserver_check is not None: + return self.ipa_params.skip_nameserver_check + ++ def __reverse_zone_name(self, ipaddress): ++ """ ++ Infer reverse zone name from an ip address. ++ ++ This function uses the same heuristics as FreeIPA to infer the zone ++ name from ip. ++ """ ++ try: ++ ip = netaddr.IPAddress(str(ipaddress)) ++ except (netaddr.AddrFormatError, ValueError): ++ net = netaddr.IPNetwork(ipaddress) ++ items = net.ip.reverse_dns.split('.') ++ prefixlen = net.prefixlen ++ ip_version = net.version ++ else: ++ items = ip.reverse_dns.split('.') ++ prefixlen = 24 if ip.version == 4 else 64 ++ ip_version = ip.version ++ if ip_version == 4: ++ return u'.'.join(items[4 - prefixlen // 8:]) ++ elif ip_version == 6: ++ return u'.'.join(items[32 - prefixlen // 4:]) ++ else: ++ self.fail_json(msg="Invalid IP version for reverse zone.") ++ + def get_zone(self, zone_name): + get_zone_args = {"idnsname": zone_name, "all": True} + response = self.api_command("dnszone_find", args=get_zone_args) +@@ -368,14 +403,33 @@ class DNSZoneModule(FreeIPABaseModule): + return zone, is_zone_active + + def get_zone_names(self): +- if len(self.ipa_params.name) > 1 and self.ipa_params.state != "absent": ++ zone_names = self.__get_zone_names_from_params() ++ if len(zone_names) > 1 and self.ipa_params.state != "absent": + self.fail_json( + msg=("Please provide a single name. Multiple values for 'name'" + "can only be supplied for state 'absent'.") + ) + ++ return zone_names ++ ++ def __get_zone_names_from_params(self): ++ if not self.ipa_params.name: ++ return [self.__reverse_zone_name(self.ipa_params.name_from_ip)] + return self.ipa_params.name + ++ def check_ipa_params(self): ++ if not self.ipa_params.name and not self.ipa_params.name_from_ip: ++ self.fail_json( ++ msg="Either `name` or `name_from_ip` must be provided." ++ ) ++ if self.ipa_params.state != "present" and self.ipa_params.name_from_ip: ++ self.fail_json( ++ msg=( ++ "Cannot use argument `name_from_ip` with state `%s`." ++ % self.ipa_params.state ++ ) ++ ) ++ + def define_ipa_commands(self): + for zone_name in self.get_zone_names(): + # Look for existing zone in IPA +@@ -434,8 +488,9 @@ def get_argument_spec(): + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), + name=dict( +- type="list", default=None, required=True, aliases=["zone_name"] ++ type="list", default=None, required=False, aliases=["zone_name"] + ), ++ name_from_ip=dict(type="str", default=None, required=False), + forwarders=dict( + type="list", + default=None, +@@ -475,7 +530,11 @@ def get_argument_spec(): + + + def main(): +- DNSZoneModule(argument_spec=get_argument_spec()).ipa_run() ++ DNSZoneModule( ++ argument_spec=get_argument_spec(), ++ mutually_exclusive=[["name", "name_from_ip"]], ++ required_one_of=[["name", "name_from_ip"]], ++ ).ipa_run() + + + if __name__ == "__main__": +diff --git a/tests/dnszone/test_dnszone_name_from_ip.yml b/tests/dnszone/test_dnszone_name_from_ip.yml +new file mode 100644 +index 0000000..9bd2eb0 +--- /dev/null ++++ b/tests/dnszone/test_dnszone_name_from_ip.yml +@@ -0,0 +1,112 @@ ++--- ++- name: Test dnszone ++ hosts: ipaserver ++ become: yes ++ gather_facts: yes ++ ++ tasks: ++ ++ # Setup ++ - name: Ensure zone is absent. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: "{{ item }}" ++ state: absent ++ with_items: ++ - 2.0.192.in-addr.arpa. ++ - 0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f.ip6.arpa. ++ - 1.0.0.0.e.f.a.c.8.b.d.0.1.0.0.2.ip6.arpa. ++ ++ # tests ++ - name: Ensure zone exists for reverse IP. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 192.0.2.3/24 ++ register: ipv4_zone ++ failed_when: not ipv4_zone.changed or ipv4_zone.failed ++ ++ - name: Ensure zone exists for reverse IP, again. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 192.0.2.3/24 ++ register: result ++ failed_when: result.changed or result.failed ++ ++ - name: Ensure zone exists for reverse IP, given the zone name. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: "{{ ipv4_zone.dnszone.name }}" ++ register: result ++ failed_when: result.changed or result.failed ++ ++ - name: Modify existing zone, using `name_from_ip`. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 192.0.2.3/24 ++ default_ttl: 1234 ++ register: result ++ failed_when: not result.changed ++ ++ - name: Modify existing zone, using `name_from_ip`, again. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 192.0.2.3/24 ++ default_ttl: 1234 ++ register: result ++ failed_when: result.changed or result.failed ++ ++ - name: Ensure ipv6 zone exists for reverse IPv6. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: fd00::0001 ++ register: ipv6_zone ++ failed_when: not ipv6_zone.changed or ipv6_zone.failed ++ ++ # - debug: ++ # msg: "{{ipv6_zone}}" ++ ++ - name: Ensure ipv6 zone was created. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: "{{ ipv6_zone.dnszone.name }}" ++ register: result ++ failed_when: result.changed or result.failed ++ ++ - name: Ensure ipv6 zone exists for reverse IPv6, again. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: fd00::0001 ++ register: result ++ failed_when: result.changed ++ ++ - name: Ensure second ipv6 zone exists for reverse IPv6. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 2001:db8:cafe:1::1 ++ register: ipv6_sec_zone ++ failed_when: not ipv6_sec_zone.changed or ipv6_zone.failed ++ ++ - name: Ensure second ipv6 zone was created. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: "{{ ipv6_sec_zone.dnszone.name }}" ++ register: result ++ failed_when: result.changed or result.failed ++ ++ - name: Ensure second ipv6 zone exists for reverse IPv6, again. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 2001:db8:cafe:1::1 ++ register: result ++ failed_when: result.changed ++ ++ # Cleanup ++ - name: Ensure zone is absent. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: "{{ item }}" ++ state: absent ++ with_items: ++ - "{{ ipv6_zone.dnszone.name }}" ++ - "{{ ipv6_sec_zone.dnszone.name }}" ++ - "{{ ipv4_zone.dnszone.name }}" +-- +2.26.2 + +From 531e544b30e69f436d14c4ce18c67998c1a0774b Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Wed, 5 Aug 2020 15:13:46 -0300 +Subject: [PATCH] Added support for client defined result data in + FReeIPABaseModule + +Modified support for processing result of IPA API commands so that +client code can define its own processing and add return values to +self.exit_args based on command result. + +If a subclass need to process the result of IPA API commands it should +override the method `process_command_result`. The default implementation +will simply evaluate if `changed` should be true. +--- + .../module_utils/ansible_freeipa_module.py | 22 +++++++++++++------ + plugins/modules/ipadnszone.py | 8 +++++++ + 2 files changed, 23 insertions(+), 7 deletions(-) + +diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py +index 4799e5a..30302b4 100644 +--- a/plugins/module_utils/ansible_freeipa_module.py ++++ b/plugins/module_utils/ansible_freeipa_module.py +@@ -619,7 +619,7 @@ class FreeIPABaseModule(AnsibleModule): + if exc_val: + self.fail_json(msg=str(exc_val)) + +- self.exit_json(changed=self.changed, user=self.exit_args) ++ self.exit_json(changed=self.changed, **self.exit_args) + + def get_command_errors(self, command, result): + """Look for erros into command results.""" +@@ -658,14 +658,22 @@ class FreeIPABaseModule(AnsibleModule): + except Exception as excpt: + self.fail_json(msg="%s: %s: %s" % (command, name, str(excpt))) + else: +- if "completed" in result: +- if result["completed"] > 0: +- self.changed = True +- else: +- self.changed = True +- ++ self.process_command_result(name, command, args, result) + self.get_command_errors(command, result) + ++ def process_command_result(self, name, command, args, result): ++ """ ++ Process an API command result. ++ ++ This method can be overriden in subclasses, and change self.exit_values ++ to return data in the result for the controller. ++ """ ++ if "completed" in result: ++ if result["completed"] > 0: ++ self.changed = True ++ else: ++ self.changed = True ++ + def require_ipa_attrs_change(self, command_args, ipa_attrs): + """ + Compare given args with current object attributes. +diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py +index 901bfef..6a90fa2 100644 +--- a/plugins/modules/ipadnszone.py ++++ b/plugins/modules/ipadnszone.py +@@ -472,6 +472,14 @@ class DNSZoneModule(FreeIPABaseModule): + } + self.add_ipa_command("dnszone_mod", zone_name, args) + ++ def process_command_result(self, name, command, args, result): ++ super(DNSZoneModule, self).process_command_result( ++ name, command, args, result ++ ) ++ if command == "dnszone_add" and self.ipa_params.name_from_ip: ++ dnszone_exit_args = self.exit_args.setdefault('dnszone', {}) ++ dnszone_exit_args['name'] = name ++ + + def get_argument_spec(): + forwarder_spec = dict( +-- +2.26.2 + +From 41e8226d0c03e06816626d78cecbc2aebf547691 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Wed, 5 Aug 2020 15:14:43 -0300 +Subject: [PATCH] Return the zone_name when adding a zone with name_from_ip. + +When adding a zone using the option name_from_ip, the user have +little control over the final name of the zone, and if this name +is to be used in further processing in a playbook it might lead to +errors if the inferred name does not match what the user wanted to. + +By returning the actual inferred zone name, the name can be safely +used for other tasks in the playbook. +--- + README-dnszone.md | 11 +++++++++++ + playbooks/dnszone/dnszone-reverse-from-ip.yml | 7 ++++++- + plugins/modules/ipadnszone.py | 8 ++++++++ + 3 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/README-dnszone.md b/README-dnszone.md +index 48b019a..3f4827b 100644 +--- a/README-dnszone.md ++++ b/README-dnszone.md +@@ -190,6 +190,17 @@ Variable | Description | Required + `skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no + + ++Return Values ++============= ++ ++ipadnszone ++---------- ++ ++Variable | Description | Returned When ++-------- | ----------- | ------------- ++`dnszone` | DNS Zone dict with zone name infered from `name_from_ip`.
Options: | If `state` is `present`, `name_from_ip` is used, and a zone was created. ++  | `name` - The name of the zone created, inferred from `name_from_ip`. | Always ++ + Authors + ======= + +diff --git a/playbooks/dnszone/dnszone-reverse-from-ip.yml b/playbooks/dnszone/dnszone-reverse-from-ip.yml +index 5693872..218a318 100644 +--- a/playbooks/dnszone/dnszone-reverse-from-ip.yml ++++ b/playbooks/dnszone/dnszone-reverse-from-ip.yml +@@ -7,4 +7,9 @@ + - name: Ensure zone exist, finding zone name from IP address. + ipadnszone: + ipaadmin_password: SomeADMINpassword +- name_from_ip: 10.1.2.3 ++ name_from_ip: 10.1.2.3/24 ++ register: result ++ ++ - name: Zone name inferred from `name_from_ip` ++ debug: ++ msg: "Zone created: {{ result.dnszone.name }}" +diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py +index 6a90fa2..93eac07 100644 +--- a/plugins/modules/ipadnszone.py ++++ b/plugins/modules/ipadnszone.py +@@ -192,6 +192,14 @@ EXAMPLES = """ + """ + + RETURN = """ ++dnszone: ++ description: DNS Zone dict with zone name infered from `name_from_ip`. ++ returned: ++ If `state` is `present`, `name_from_ip` is used, and a zone was created. ++ options: ++ name: ++ description: The name of the zone created, inferred from `name_from_ip`. ++ returned: always + """ + + from ipapython.dnsutil import DNSName # noqa: E402 +-- +2.26.2 + +From 46bbc7bbd7a4e01d07b0390aee8c799aaa5ac895 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Mon, 17 Aug 2020 15:52:38 -0300 +Subject: [PATCH] Document usage of `name_from_ip`. + +Since `name_from_ip` has a similar, but not equal, behavior to `name`, +and as the inferred DNS zone might depend on DNS configuration and +can be different than the user expects, it has some limited usage, +and the user must be aware of its effects. + +This change to the documentation enhance the documentation including +more details on the attribute usage. +--- + README-dnszone.md | 42 ++++++++++++++++++++++++++++++++++- + plugins/modules/ipadnszone.py | 4 +++- + 2 files changed, 44 insertions(+), 2 deletions(-) + +diff --git a/README-dnszone.md b/README-dnszone.md +index 3f4827b..c5a7ab3 100644 +--- a/README-dnszone.md ++++ b/README-dnszone.md +@@ -152,6 +152,46 @@ Example playbook to remove a zone: + + ``` + ++Example playbook to create a zone for reverse DNS lookup, from an IP address: ++ ++```yaml ++ ++--- ++- name: dnszone present ++ hosts: ipaserver ++ become: true ++ ++ tasks: ++ - name: Ensure zone for reverse DNS lookup is present. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 192.168.1.2 ++ state: present ++``` ++ ++Note that, on the previous example the zone created with `name_from_ip` might be "1.168.192.in-addr.arpa.", "168.192.in-addr.arpa.", or "192.in-addr.arpa.", depending on the DNS response the system get while querying for zones, and for this reason, when creating a zone using `name_from_ip`, the inferred zone name is returned to the controller, in the attribute `dnszone.name`. Since the zone inferred might not be what a user expects, `name_from_ip` can only be used with `state: present`. To have more control over the zone name, the prefix length for the IP address can be provided. ++ ++Example playbook to create a zone for reverse DNS lookup, from an IP address, given the prefix length and displaying the resulting zone name: ++ ++```yaml ++ ++--- ++- name: dnszone present ++ hosts: ipaserver ++ become: true ++ ++ tasks: ++ - name: Ensure zone for reverse DNS lookup is present. ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name_from_ip: 192.168.1.2/24 ++ state: present ++ register: result ++ - name: Display inferred zone name. ++ debug: ++ msg: "Zone name: {{ result.dnszone.name }}" ++``` ++ + + Variables + ========= +@@ -164,7 +204,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 + `name` \| `zone_name` | The zone name string or list of strings. | no +-`name_from_ip` | Derive zone name from reverse of IP (PTR). | no ++`name_from_ip` | Derive zone name from reverse of IP (PTR). Can only be used with `state: present`. | no + `forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no +   | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes +   | `port` - The custom port that should be used on this server. | no +diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py +index 93eac07..ff6bfff 100644 +--- a/plugins/modules/ipadnszone.py ++++ b/plugins/modules/ipadnszone.py +@@ -44,7 +44,9 @@ options: + type: list + alises: ["zone_name"] + name_from_ip: +- description: Derive zone name from reverse of IP (PTR). ++ description: | ++ Derive zone name from reverse of IP (PTR). ++ Can only be used with `state: present`. + required: false + type: str + forwarders: +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-Add-suppport-for-changing-password-of-symmetric-vaults_rhbz#1839197.patch b/SOURCES/ansible-freeipa-0.1.12-Add-suppport-for-changing-password-of-symmetric-vaults_rhbz#1839197.patch new file mode 100644 index 0000000..8ff43ea --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Add-suppport-for-changing-password-of-symmetric-vaults_rhbz#1839197.patch @@ -0,0 +1,435 @@ +From 78b635ae78346fdfb298dd0d0c82ae1ff34b754a Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Tue, 23 Jun 2020 17:53:47 -0300 +Subject: [PATCH] Add suppport for changing password of symmetric vaults. + +Allows changing passwords of symmetric waults, using a new variable +`new_password` (or the file-base version, `new_password_file`). The +old password must be passed using the `password` or `password_file` +variables that also received new aliases `old_password` and +`old_password_file`, respectively. + +Tests were modyfied to reflect the changes. +--- + README-vault.md | 23 +++- + .../vault/change-password-symmetric-vault.yml | 2 +- + plugins/modules/ipavault.py | 129 +++++++++++++++--- + tests/vault/test_vault_symmetric.yml | 64 +++++++++ + 4 files changed, 194 insertions(+), 24 deletions(-) + +diff --git a/README-vault.md b/README-vault.md +index c7ae6916..fa1d3e11 100644 +--- a/README-vault.md ++++ b/README-vault.md +@@ -165,6 +165,22 @@ Example playbook to make sure vault data is absent in a symmetric vault: + state: absent + ``` + ++Example playbook to change the password of a symmetric: ++ ++```yaml ++--- ++- name: Playbook to handle vaults ++ hosts: ipaserver ++ become: true ++ ++ tasks: ++ - ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ old_password: SomeVAULTpassword ++ new_password: SomeNEWpassword ++``` ++ + Example playbook to make sure vault is absent: + + ```yaml +@@ -197,8 +213,11 @@ Variable | Description | Required + `name` \| `cn` | The list of vault name strings. | yes + `description` | The vault description string. | no + `nomembers` | Suppress processing of membership attributes. (bool) | no +-`password ` \| `vault_password` \| `ipavaultpassword` | Vault password. | no +-`public_key ` \| `vault_public_key` \| `ipavaultpublickey` | Base64 encoded vault public key. | no ++`password` \| `vault_password` \| `ipavaultpassword` \| `old_password`| Vault password. | no ++`password_file` \| `vault_password_file` \| `old_password_file`| File containing Base64 encoded Vault password. | no ++`new_password` | Vault new password. | no ++`new_password_file` | File containing Base64 encoded new Vault password. | no ++`public_key ` \| `vault_public_key` \| `old_password_file` | Base64 encoded vault public key. | no + `public_key_file` \| `vault_public_key_file` | Path to file with public key. | no + `private_key `\| `vault_private_key` | Base64 encoded vault private key. Used only to retrieve data. | no + `private_key_file` \| `vault_private_key_file` | Path to file with private key. Used only to retrieve data. | no +diff --git a/playbooks/vault/change-password-symmetric-vault.yml b/playbooks/vault/change-password-symmetric-vault.yml +index 3871f45d..396a79f6 100644 +--- a/playbooks/vault/change-password-symmetric-vault.yml ++++ b/playbooks/vault/change-password-symmetric-vault.yml +@@ -10,7 +10,7 @@ + ipaadmin_password: SomeADMINpassword + name: symvault + password: SomeVAULTpassword +- - name: Change vault passord. ++ - name: Change vault password. + ipavault: + ipaadmin_password: SomeADMINpassword + name: symvault +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index ad5dd413..46c6fcdb 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -69,12 +69,20 @@ + description: password to be used on symmetric vault. + required: false + type: string +- aliases: ["ipavaultpassword", "vault_password"] ++ aliases: ["ipavaultpassword", "vault_password", "old_password"] + password_file: + description: file with password to be used on symmetric vault. + required: false + type: string +- aliases: ["vault_password_file"] ++ aliases: ["vault_password_file", "old_password_file"] ++ new_password: ++ description: new password to be used on symmetric vault. ++ required: false ++ type: string ++ new_password_file: ++ description: file with new password to be used on symmetric vault. ++ required: false ++ type: string + salt: + description: Vault salt. + required: false +@@ -235,7 +243,15 @@ + state: retrieved + register: result + - debug: +- msg: "{{ result.data | b64decode }}" ++ msg: "{{ result.data }}" ++ ++# Change password of a symmetric vault ++- ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ username: admin ++ old_password: SomeVAULTpassword ++ new_password: SomeNEWpassword + + # Ensure vault symvault is absent + - ipavault: +@@ -416,18 +432,29 @@ def check_parameters(module, state, action, description, username, service, + shared, users, groups, services, owners, ownergroups, + ownerservices, vault_type, salt, password, password_file, + public_key, public_key_file, private_key, +- private_key_file, vault_data, datafile_in, datafile_out): ++ private_key_file, vault_data, datafile_in, datafile_out, ++ new_password, new_password_file): + invalid = [] + if state == "present": + invalid = ['private_key', 'private_key_file', 'datafile_out'] + ++ if all([password, password_file]) \ ++ or all([new_password, new_password_file]): ++ module.fail_json(msg="Password specified multiple times.") ++ ++ if any([new_password, new_password_file]) \ ++ and not any([password, password_file]): ++ module.fail_json( ++ msg="Either `password` or `password_file` must be provided to " ++ "change symmetric vault password.") ++ + if action == "member": + invalid.extend(['description']) + + elif state == "absent": + invalid = ['description', 'salt', 'vault_type', 'private_key', + 'private_key_file', 'datafile_in', 'datafile_out', +- 'vault_data'] ++ 'vault_data', 'new_password', 'new_password_file'] + + if action == "vault": + invalid.extend(['users', 'groups', 'services', 'owners', +@@ -437,7 +464,7 @@ def check_parameters(module, state, action, description, username, service, + elif state == "retrieved": + invalid = ['description', 'salt', 'datafile_in', 'users', 'groups', + 'owners', 'ownergroups', 'public_key', 'public_key_file', +- 'vault_data'] ++ 'vault_data', 'new_password', 'new_password_file'] + if action == 'member': + module.fail_json( + msg="State `retrieved` do not support action `member`.") +@@ -458,11 +485,17 @@ def check_parameters(module, state, action, description, username, service, + def check_encryption_params(module, state, action, vault_type, salt, + password, password_file, public_key, + public_key_file, private_key, private_key_file, +- vault_data, datafile_in, datafile_out, res_find): ++ vault_data, datafile_in, datafile_out, ++ new_password, new_password_file, res_find): + vault_type_invalid = [] ++ ++ if res_find is not None: ++ vault_type = res_find['ipavaulttype'] ++ + if vault_type == "standard": + vault_type_invalid = ['public_key', 'public_key_file', 'password', +- 'password_file', 'salt'] ++ 'password_file', 'salt', 'new_password', ++ 'new_password_file'] + + if vault_type is None or vault_type == "symmetric": + vault_type_invalid = ['public_key', 'public_key_file', +@@ -473,8 +506,14 @@ def check_encryption_params(module, state, action, vault_type, salt, + msg="Symmetric vault requires password or password_file " + "to store data or change `salt`.") + ++ if any([new_password, new_password_file]) and res_find is None: ++ module.fail_json( ++ msg="Cannot modify password of inexistent vault.") ++ + if vault_type == "asymmetric": +- vault_type_invalid = ['password', 'password_file'] ++ vault_type_invalid = [ ++ 'password', 'password_file', 'new_password', 'new_password_file' ++ ] + if not any([public_key, public_key_file]) and res_find is None: + module.fail_json( + msg="Assymmetric vault requires public_key " +@@ -487,6 +526,43 @@ def check_encryption_params(module, state, action, vault_type, salt, + (param, vault_type or 'symmetric')) + + ++def change_password(module, res_find, password, password_file, new_password, ++ new_password_file): ++ """ ++ Change the password of a symmetric vault. ++ ++ To change the password of a vault, it is needed to retrieve the stored ++ data with the current password, and store the data again, with the new ++ password, forcing it to override the old one. ++ """ ++ # verify parameters. ++ if not any([new_password, new_password_file]): ++ return [] ++ if res_find["ipavaulttype"][0] != "symmetric": ++ module.fail_json(msg="Cannot change password of `%s` vault." ++ % res_find["ipavaulttype"]) ++ ++ # prepare arguments to retrieve data. ++ name = res_find["cn"][0] ++ args = {} ++ if password: ++ args["password"] = password ++ if password_file: ++ args["password"] = password_file ++ # retrieve current stored data ++ result = api_command(module, 'vault_retrieve', name, args) ++ args['data'] = result['result']['data'] ++ ++ # modify arguments to store data with new password. ++ if password: ++ args["password"] = new_password ++ if password_file: ++ args["password"] = new_password_file ++ args["override_password"] = True ++ # return the command to store data with the new password. ++ return [(name, "vault_archive", args)] ++ ++ + def main(): + ansible_module = AnsibleModule( + argument_spec=dict( +@@ -533,10 +609,18 @@ def main(): + datafile_out=dict(type="str", required=False, default=None, + aliases=['out']), + vault_password=dict(type="str", required=False, default=None, +- aliases=['ipavaultpassword', 'password'], +- no_log=True), ++ no_log=True, ++ aliases=['ipavaultpassword', 'password', ++ "old_password"]), + vault_password_file=dict(type="str", required=False, default=None, +- no_log=False, aliases=['password_file']), ++ no_log=False, ++ aliases=[ ++ 'password_file', "old_password_file" ++ ]), ++ new_password=dict(type="str", required=False, default=None, ++ no_log=True), ++ new_password_file=dict(type="str", required=False, default=None, ++ no_log=False), + # state + action=dict(type="str", default="vault", + choices=["vault", "data", "member"]), +@@ -546,6 +630,7 @@ def main(): + supports_check_mode=True, + mutually_exclusive=[['username', 'service', 'shared'], + ['datafile_in', 'vault_data'], ++ ['new_password', 'new_password_file'], + ['vault_password', 'vault_password_file'], + ['vault_public_key', 'vault_public_key_file']], + ) +@@ -576,6 +661,8 @@ def main(): + salt = module_params_get(ansible_module, "vault_salt") + password = module_params_get(ansible_module, "vault_password") + password_file = module_params_get(ansible_module, "vault_password_file") ++ new_password = module_params_get(ansible_module, "new_password") ++ new_password_file = module_params_get(ansible_module, "new_password_file") + public_key = module_params_get(ansible_module, "vault_public_key") + public_key_file = module_params_get(ansible_module, + "vault_public_key_file") +@@ -614,7 +701,8 @@ def main(): + service, shared, users, groups, services, owners, + ownergroups, ownerservices, vault_type, salt, password, + password_file, public_key, public_key_file, private_key, +- private_key_file, vault_data, datafile_in, datafile_out) ++ private_key_file, vault_data, datafile_in, datafile_out, ++ new_password, new_password_file) + # Init + + changed = False +@@ -660,7 +748,7 @@ def main(): + ansible_module, state, action, vault_type, salt, password, + password_file, public_key, public_key_file, private_key, + private_key_file, vault_data, datafile_in, datafile_out, +- res_find) ++ new_password, new_password_file, res_find) + + # Found the vault + if action == "vault": +@@ -721,7 +809,6 @@ def main(): + owner_add_args = gen_member_args( + args, owner_add, ownergroups_add, ownerservice_add) + if owner_add_args is not None: +- # ansible_module.warn("OWNER ADD: %s" % owner_add_args) + commands.append( + [name, 'vault_add_owner', owner_add_args]) + +@@ -729,7 +816,6 @@ def main(): + owner_del_args = gen_member_args( + args, owner_del, ownergroups_del, ownerservice_del) + if owner_del_args is not None: +- # ansible_module.warn("OWNER DEL: %s" % owner_del_args) + commands.append( + [name, 'vault_remove_owner', owner_del_args]) + +@@ -758,19 +844,22 @@ def main(): + if any([vault_data, datafile_in]): + commands.append([name, "vault_archive", pwdargs]) + ++ cmds = change_password( ++ ansible_module, res_find, password, password_file, ++ new_password, new_password_file) ++ commands.extend(cmds) ++ + elif state == "retrieved": + if res_find is None: + ansible_module.fail_json( + msg="Vault `%s` not found to retrieve data." % name) + +- vault_type = res_find['cn'] +- + # verify data encription args + check_encryption_params( + ansible_module, state, action, vault_type, salt, password, + password_file, public_key, public_key_file, private_key, + private_key_file, vault_data, datafile_in, datafile_out, +- res_find) ++ new_password, new_password_file, res_find) + + pwdargs = data_storage_args( + args, vault_data, password, password_file, private_key, +@@ -813,7 +902,6 @@ def main(): + errors = [] + for name, command, args in commands: + try: +- # ansible_module.warn("RUN: %s %s %s" % (command, name, args)) + result = api_command(ansible_module, command, name, args) + + if command == 'vault_archive': +@@ -829,7 +917,6 @@ def main(): + raise Exception("No data retrieved.") + changed = False + else: +- # ansible_module.warn("RESULT: %s" % (result)) + if "completed" in result: + if result["completed"] > 0: + changed = True +diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml +index c9429f4f..a6072d88 100644 +--- a/tests/vault/test_vault_symmetric.yml ++++ b/tests/vault/test_vault_symmetric.yml +@@ -178,6 +178,61 @@ + register: result + failed_when: result.data != 'Hello World.' or result.changed + ++ - name: Change vault password. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ password: SomeVAULTpassword ++ new_password: SomeNEWpassword ++ register: result ++ failed_when: not result.changed ++ ++ - name: Retrieve data from symmetric vault, with wrong password. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ password: SomeVAULTpassword ++ state: retrieved ++ register: result ++ failed_when: not result.failed or "Invalid credentials" not in result.msg ++ ++ - name: Change vault password, with wrong `old_password`. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ password: SomeVAULTpassword ++ new_password: SomeNEWpassword ++ register: result ++ failed_when: not result.failed or "Invalid credentials" not in result.msg ++ ++ - name: Retrieve data from symmetric vault, with new password. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ password: SomeNEWpassword ++ state: retrieved ++ register: result ++ failed_when: result.data != 'Hello World.' or result.changed ++ ++ - name: Try to add vault with multiple passwords. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: inexistentvault ++ password: SomeVAULTpassword ++ password_file: "{{ ansible_env.HOME }}/password.txt" ++ register: result ++ failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg ++ ++ - name: Try to add vault with multiple new passwords. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: inexistentvault ++ password: SomeVAULTpassword ++ new_password: SomeVAULTpassword ++ new_password_file: "{{ ansible_env.HOME }}/password.txt" ++ register: result ++ failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg ++ + - name: Ensure symmetric vault is absent + ipavault: + ipaadmin_password: SomeADMINpassword +@@ -194,5 +249,14 @@ + register: result + failed_when: result.changed + ++ - name: Try to change password of inexistent vault. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: inexistentvault ++ password: SomeVAULTpassword ++ new_password: SomeNEWpassword ++ register: result ++ failed_when: not result.failed or "Cannot modify password of inexistent vault" not in result.msg ++ + - name: Cleanup testing environment. + import_tasks: env_cleanup.yml diff --git a/SOURCES/ansible-freeipa-0.1.12-Allow-multiple-dns-zones-to-be-absent_rhbz#1845058.patch b/SOURCES/ansible-freeipa-0.1.12-Allow-multiple-dns-zones-to-be-absent_rhbz#1845058.patch new file mode 100644 index 0000000..76ae37f --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Allow-multiple-dns-zones-to-be-absent_rhbz#1845058.patch @@ -0,0 +1,302 @@ +From 75d16c2da4a5621943873a26343eb0f2acc2a925 Mon Sep 17 00:00:00 2001 +From: Sergio Oliveira Campos +Date: Mon, 3 Aug 2020 11:54:44 -0300 +Subject: [PATCH] Allow multiple dns zones to be absent. + +This PR allow ipadnszone module to ensure that multiple dns zones +are absent at once, to be consistent with other ansible-freeipa +modules. + +To fix this issue, it was required that custom arguents must be +passed using keyword arguments so that `get_ipa_command_args()` +is kept generic. +--- + README-dnszone.md | 2 +- + .../module_utils/ansible_freeipa_module.py | 4 +- + plugins/modules/ipadnszone.py | 126 ++++++++++-------- + tests/dnszone/test_dnszone.yml | 37 +++++ + 4 files changed, 107 insertions(+), 62 deletions(-) + +diff --git a/README-dnszone.md b/README-dnszone.md +index 766efe5..9c9b12c 100644 +--- a/README-dnszone.md ++++ b/README-dnszone.md +@@ -163,7 +163,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 +-`name` \| `zone_name` | The zone name string. | yes ++`name` \| `zone_name` | The zone name string or list of strings. | yes + `forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no +   | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes +   | `port` - The custom port that should be used on this server. | no +diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py +index 122ea2e..1e55693 100644 +--- a/plugins/module_utils/ansible_freeipa_module.py ++++ b/plugins/module_utils/ansible_freeipa_module.py +@@ -506,7 +506,7 @@ class FreeIPABaseModule(AnsibleModule): + # when needed. + self.ipa_params = AnsibleFreeIPAParams(self) + +- def get_ipa_command_args(self): ++ def get_ipa_command_args(self, **kwargs): + """ + Return a dict to be passed to an IPA command. + +@@ -538,7 +538,7 @@ class FreeIPABaseModule(AnsibleModule): + elif hasattr(self, param_name): + method = getattr(self, param_name) + if callable(method): +- value = method() ++ value = method(**kwargs) + + # We don't have a way to guess the value so fail. + else: +diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py +index 717978e..c5e812a 100644 +--- a/plugins/modules/ipadnszone.py ++++ b/plugins/modules/ipadnszone.py +@@ -41,7 +41,7 @@ options: + name: + description: The zone name string. + required: true +- type: str ++ type: list + alises: ["zone_name"] + forwarders: + description: The list of global DNS forwarders. +@@ -268,7 +268,7 @@ class DNSZoneModule(FreeIPABaseModule): + + return True + +- def get_ipa_nsec3paramrecord(self): ++ def get_ipa_nsec3paramrecord(self, **kwargs): + nsec3param_rec = self.ipa_params.nsec3param_rec + if nsec3param_rec is not None: + error_msg = ( +@@ -280,7 +280,7 @@ class DNSZoneModule(FreeIPABaseModule): + self.fail_json(msg=error_msg) + return nsec3param_rec + +- def get_ipa_idnsforwarders(self): ++ def get_ipa_idnsforwarders(self, **kwargs): + if self.ipa_params.forwarders is not None: + forwarders = [] + for forwarder in self.ipa_params.forwarders: +@@ -304,14 +304,14 @@ class DNSZoneModule(FreeIPABaseModule): + + return forwarders + +- def get_ipa_idnsallowtransfer(self): ++ def get_ipa_idnsallowtransfer(self, **kwargs): + if self.ipa_params.allow_transfer is not None: + error_msg = "Invalid ip_address for DNS allow_transfer: %s" + self.validate_ips(self.ipa_params.allow_transfer, error_msg) + + return (";".join(self.ipa_params.allow_transfer) or "none") + ";" + +- def get_ipa_idnsallowquery(self): ++ def get_ipa_idnsallowquery(self, **kwargs): + if self.ipa_params.allow_query is not None: + error_msg = "Invalid ip_address for DNS allow_query: %s" + self.validate_ips(self.ipa_params.allow_query, error_msg) +@@ -334,81 +334,89 @@ class DNSZoneModule(FreeIPABaseModule): + + return ".".join((name, domain)) + +- def get_ipa_idnssoarname(self): ++ def get_ipa_idnssoarname(self, **kwargs): + if self.ipa_params.admin_email is not None: + return DNSName( + self._replace_at_symbol_in_rname(self.ipa_params.admin_email) + ) + +- def get_ipa_idnssoamname(self): ++ def get_ipa_idnssoamname(self, **kwargs): + if self.ipa_params.name_server is not None: + return DNSName(self.ipa_params.name_server) + +- def get_ipa_skip_overlap_check(self): +- if not self.zone and self.ipa_params.skip_overlap_check is not None: ++ def get_ipa_skip_overlap_check(self, **kwargs): ++ zone = kwargs.get('zone') ++ if not zone and self.ipa_params.skip_overlap_check is not None: + return self.ipa_params.skip_overlap_check + +- def get_ipa_skip_nameserver_check(self): +- if not self.zone and self.ipa_params.skip_nameserver_check is not None: ++ def get_ipa_skip_nameserver_check(self, **kwargs): ++ zone = kwargs.get('zone') ++ if not zone and self.ipa_params.skip_nameserver_check is not None: + return self.ipa_params.skip_nameserver_check + + def get_zone(self, zone_name): + get_zone_args = {"idnsname": zone_name, "all": True} + response = self.api_command("dnszone_find", args=get_zone_args) + ++ zone = None ++ is_zone_active = False ++ + if response["count"] == 1: +- self.zone = response["result"][0] +- self.is_zone_active = self.zone.get("idnszoneactive") == ["TRUE"] +- return self.zone ++ zone = response["result"][0] ++ is_zone_active = zone.get("idnszoneactive") == ["TRUE"] + +- # Zone doesn't exist yet +- self.zone = None +- self.is_zone_active = False ++ return zone, is_zone_active ++ ++ def get_zone_names(self): ++ if len(self.ipa_params.name) > 1 and self.ipa_params.state != "absent": ++ self.fail_json( ++ msg=("Please provide a single name. Multiple values for 'name'" ++ "can only be supplied for state 'absent'.") ++ ) + +- @property +- def zone_name(self): + return self.ipa_params.name + + def define_ipa_commands(self): +- # Look for existing zone in IPA +- self.get_zone(self.zone_name) +- args = self.get_ipa_command_args() +- just_added = False +- +- if self.ipa_params.state in ["present", "enabled", "disabled"]: +- if not self.zone: +- # Since the zone doesn't exist we just create it +- # with given args +- self.add_ipa_command("dnszone_add", self.zone_name, args) +- self.is_zone_active = True +- just_added = True +- +- else: +- # Zone already exist so we need to verify if given args +- # matches the current config. If not we updated it. +- if self.require_ipa_attrs_change(args, self.zone): +- self.add_ipa_command("dnszone_mod", self.zone_name, args) +- +- if self.ipa_params.state == "enabled" and not self.is_zone_active: +- self.add_ipa_command("dnszone_enable", self.zone_name) +- +- if self.ipa_params.state == "disabled" and self.is_zone_active: +- self.add_ipa_command("dnszone_disable", self.zone_name) +- +- if self.ipa_params.state == "absent": +- if self.zone: +- self.add_ipa_command("dnszone_del", self.zone_name) +- +- # Due to a bug in FreeIPA dnszone-add won't set +- # SOA Serial. The good news is that dnszone-mod does the job. +- # See: https://pagure.io/freeipa/issue/8227 +- # Because of that, if the zone was just added with a given serial +- # we run mod just after to workaround the bug +- if just_added and self.ipa_params.serial is not None: +- args = { +- "idnssoaserial": self.ipa_params.serial, +- } +- self.add_ipa_command("dnszone_mod", self.zone_name, args) ++ for zone_name in self.get_zone_names(): ++ # Look for existing zone in IPA ++ zone, is_zone_active = self.get_zone(zone_name) ++ args = self.get_ipa_command_args(zone=zone) ++ just_added = False ++ ++ if self.ipa_params.state in ["present", "enabled", "disabled"]: ++ if not zone: ++ # Since the zone doesn't exist we just create it ++ # with given args ++ self.add_ipa_command("dnszone_add", zone_name, args) ++ is_zone_active = True ++ just_added = True ++ ++ else: ++ # Zone already exist so we need to verify if given args ++ # matches the current config. If not we updated it. ++ if self.require_ipa_attrs_change(args, zone): ++ self.add_ipa_command("dnszone_mod", zone_name, args) ++ ++ if self.ipa_params.state == "enabled" and not is_zone_active: ++ self.add_ipa_command("dnszone_enable", zone_name) ++ ++ if self.ipa_params.state == "disabled" and is_zone_active: ++ self.add_ipa_command("dnszone_disable", zone_name) ++ ++ if self.ipa_params.state == "absent": ++ if zone: ++ self.add_ipa_command("dnszone_del", zone_name) ++ ++ # Due to a bug in FreeIPA dnszone-add won't set ++ # SOA Serial. The good news is that dnszone-mod does the job. ++ # See: https://pagure.io/freeipa/issue/8227 ++ # Because of that, if the zone was just added with a given serial ++ # we run mod just after to workaround the bug ++ if just_added and self.ipa_params.serial is not None: ++ args = { ++ "idnssoaserial": self.ipa_params.serial, ++ } ++ self.add_ipa_command("dnszone_mod", zone_name, args) + + + def get_argument_spec(): +@@ -426,7 +434,7 @@ def get_argument_spec(): + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), + name=dict( +- type="str", default=None, required=True, aliases=["zone_name"] ++ type="list", default=None, required=True, aliases=["zone_name"] + ), + forwarders=dict( + type="list", +diff --git a/tests/dnszone/test_dnszone.yml b/tests/dnszone/test_dnszone.yml +index f7bd1f0..bd820df 100644 +--- a/tests/dnszone/test_dnszone.yml ++++ b/tests/dnszone/test_dnszone.yml +@@ -149,3 +149,40 @@ + forwarders: [] + register: result + failed_when: not result.changed ++ ++ - name: Create zones test1 ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: test1.testzone.local ++ ++ - name: Create zones test2 ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: test2.testzone.local ++ ++ - name: Create zones test3 ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: test3.testzone.local ++ ++ - name: Ensure multiple zones are absent ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - test1.testzone.local ++ - test2.testzone.local ++ - test3.testzone.local ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Ensure multiple zones are absent, again ++ ipadnszone: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - test1.testzone.local ++ - test2.testzone.local ++ - test3.testzone.local ++ state: absent ++ register: result ++ failed_when: result.changed +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-Fix-allow_retrieve_keytab_host-in-service-module_rhbz#1868020.patch b/SOURCES/ansible-freeipa-0.1.12-Fix-allow_retrieve_keytab_host-in-service-module_rhbz#1868020.patch new file mode 100644 index 0000000..1211395 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Fix-allow_retrieve_keytab_host-in-service-module_rhbz#1868020.patch @@ -0,0 +1,628 @@ +# Skipping 3ab575bcac310166e7d29c5a5349d90482f4e629 as it is reorganizing +# service module test test_service.yml and +# test_service_without_skip_host_check.yml + +From b5e93c705fc56f6592121aa09bfb9f6dce5cee35 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Tue, 11 Aug 2020 16:23:15 -0300 +Subject: [PATCH] Fix `allow_retrieve_keytab_host` in service module. + +The attribute `allow_retrieve_keytab_host` was not working due to +wrong processing of the input and verification if the values should +be updated. Both the issues are fixed by this change. + +Tests were added to better verify service keytab members. +--- + plugins/modules/ipaservice.py | 4 +- + tests/service/env_cleanup.yml | 68 +++++ + tests/service/env_setup.yml | 73 +++++ + tests/service/env_vars.yml | 15 + + tests/service/test_service_keytab.yml | 397 ++++++++++++++++++++++++++ + 5 files changed, 555 insertions(+), 2 deletions(-) + create mode 100644 tests/service/env_cleanup.yml + create mode 100644 tests/service/env_setup.yml + create mode 100644 tests/service/env_vars.yml + create mode 100644 tests/service/test_service_keytab.yml + +diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py +index b0d2535..8bc390d 100644 +--- a/plugins/modules/ipaservice.py ++++ b/plugins/modules/ipaservice.py +@@ -460,7 +460,7 @@ def main(): + allow_retrieve_keytab_group = module_params_get( + ansible_module, "allow_retrieve_keytab_group") + allow_retrieve_keytab_host = module_params_get( +- ansible_module, "allow_create_keytab_host") ++ ansible_module, "allow_retrieve_keytab_host") + allow_retrieve_keytab_hostgroup = module_params_get( + ansible_module, "allow_retrieve_keytab_hostgroup") + delete_continue = module_params_get(ansible_module, "delete_continue") +@@ -727,7 +727,7 @@ def main(): + # Allow retrieve keytab + if len(allow_retrieve_keytab_user_add) > 0 or \ + len(allow_retrieve_keytab_group_add) > 0 or \ +- len(allow_retrieve_keytab_hostgroup_add) > 0 or \ ++ len(allow_retrieve_keytab_host_add) > 0 or \ + len(allow_retrieve_keytab_hostgroup_add) > 0: + commands.append( + [name, "service_allow_retrieve_keytab", +diff --git a/tests/service/env_cleanup.yml b/tests/service/env_cleanup.yml +new file mode 100644 +index 0000000..f96a75b +--- /dev/null ++++ b/tests/service/env_cleanup.yml +@@ -0,0 +1,68 @@ ++--- ++# Cleanup tasks for the service module tests. ++- name: Ensure services are absent. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - "HTTP/{{ svc_fqdn }}" ++ - "HTTP/{{ nohost_fqdn }}" ++ - HTTP/svc.ihavenodns.info ++ - HTTP/no.idontexist.local ++ - "cifs/{{ host1_fqdn }}" ++ state: absent ++ ++- name: Ensure host "{{ svc_fqdn }}" is absent ++ ipahost: ++ ipaadmin_password: SomeADMINpassword ++ name: "{{ svc_fqdn }}" ++ update_dns: yes ++ state: absent ++ ++- name: Ensure host is absent ++ ipahost: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ - "{{ nohost_fqdn }}" ++ - svc.ihavenodns.info ++ update_dns: no ++ state: absent ++ ++- name: Ensure testing users are absent. ++ ipauser: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - user01 ++ - user02 ++ state: absent ++ ++- name: Ensure testing groups are absent. ++ ipagroup: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - group01 ++ - group02 ++ state: absent ++ ++- name: Ensure testing hostgroup hostgroup01 is absent. ++ ipagroup: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - hostgroup01 ++ state: absent ++ ++- name: Ensure testing hostgroup hostgroup02 is absent. ++ ipagroup: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - hostgroup02 ++ state: absent ++ ++- name: Remove IP address for "nohost" host. ++ ipadnsrecord: ++ ipaadmin_password: SomeADMINpassword ++ zone_name: "{{ test_domain }}." ++ name: nohost ++ del_all: yes ++ state: absent +diff --git a/tests/service/env_setup.yml b/tests/service/env_setup.yml +new file mode 100644 +index 0000000..309cfc0 +--- /dev/null ++++ b/tests/service/env_setup.yml +@@ -0,0 +1,73 @@ ++# Setup environment for service module tests. ++--- ++- name: Setup variables and facts. ++ include_tasks: env_vars.yml ++ ++# Cleanup before setup. ++- name: Cleanup test environment. ++ include_tasks: env_cleanup.yml ++ ++- name: Add IP address for "nohost" host. ++ ipadnsrecord: ++ ipaadmin_password: SomeADMINpassword ++ zone_name: "{{ test_domain }}." ++ name: nohost ++ a_ip_address: "{{ ipv4_prefix + '.100' }}" ++ ++- name: Add hosts for tests. ++ ipahost: ++ ipaadmin_password: SomeADMINpassword ++ hosts: ++ - name: "{{ host1_fqdn }}" ++ ip_address: "{{ ipv4_prefix + '.101' }}" ++ - name: "{{ host2_fqdn }}" ++ ip_address: "{{ ipv4_prefix + '.102' }}" ++ - name: "{{ svc_fqdn }}" ++ ip_address: "{{ ipv4_prefix + '.201' }}" ++ - name: svc.ihavenodns.info ++ force: yes ++ update_dns: yes ++ ++- name: Ensure testing user user01 is present. ++ ipauser: ++ ipaadmin_password: SomeADMINpassword ++ name: user01 ++ first: user01 ++ last: last ++ ++- name: Ensure testing user user02 is present. ++ ipauser: ++ ipaadmin_password: SomeADMINpassword ++ name: user02 ++ first: user02 ++ last: last ++ ++- name: Ensure testing group group01 is present. ++ ipagroup: ++ ipaadmin_password: SomeADMINpassword ++ name: group01 ++ ++- name: Ensure testing group group02 is present. ++ ipagroup: ++ ipaadmin_password: SomeADMINpassword ++ name: group02 ++ ++- name: Ensure testing hostgroup hostgroup01 is present. ++ ipahostgroup: ++ ipaadmin_password: SomeADMINpassword ++ name: hostgroup01 ++ ++- name: Ensure testing hostgroup hostgroup02 is present. ++ ipahostgroup: ++ ipaadmin_password: SomeADMINpassword ++ name: hostgroup02 ++ ++- name: Ensure services are absent. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: ++ - "HTTP/{{ svc_fqdn }}" ++ - "HTTP/{{ nohost_fqdn }}" ++ - HTTP/svc.ihavenodns.info ++ - HTTP/no.idontexist.info ++ state: absent +diff --git a/tests/service/env_vars.yml b/tests/service/env_vars.yml +new file mode 100644 +index 0000000..eb53c7a +--- /dev/null ++++ b/tests/service/env_vars.yml +@@ -0,0 +1,15 @@ ++--- ++ - name: Get Domain from server name ++ set_fact: ++ test_domain: "{{ ansible_fqdn.split('.')[1:] | join('.') }}" ++ ++ - name: Set host1, host2 and svc hosts fqdn ++ set_fact: ++ host1_fqdn: "{{ 'host1.' + test_domain }}" ++ host2_fqdn: "{{ 'host2.' + test_domain }}" ++ svc_fqdn: "{{ 'svc.' + test_domain }}" ++ nohost_fqdn: "{{ 'nohost.' + test_domain }}" ++ ++ - name: Get IPv4 address prefix from server node ++ set_fact: ++ ipv4_prefix: "{{ ansible_default_ipv4.address.split('.')[:-1] | join('.') }}" +diff --git a/tests/service/test_service_keytab.yml b/tests/service/test_service_keytab.yml +new file mode 100644 +index 0000000..0918802 +--- /dev/null ++++ b/tests/service/test_service_keytab.yml +@@ -0,0 +1,397 @@ ++--- ++- name: Test service ++ hosts: ipaserver ++ become: yes ++ ++ tasks: ++ # setup ++ - name: Setup test envirnoment. ++ include_tasks: env_setup.yml ++ ++ # Add service to test keytab create/retrieve attributes. ++ - name: Ensure test service is present ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ pac_type: ++ - MS-PAC ++ - PAD ++ auth_ind: otp ++ force: yes ++ requires_pre_auth: yes ++ ok_as_delegate: no ++ ok_to_auth_as_delegate: no ++ ++ # tests ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for users. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for users, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for users. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for users, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for group. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for group, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for group. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for group, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for host. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for host, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for host. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for host, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for hostgroup. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for hostgroup, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for hostgroup. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ state: absent ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for hostgroup, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_create_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for users. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for users, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for users. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for users, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_user: ++ - user01 ++ - user02 ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for group. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for group, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for group. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for group, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_group: ++ - group01 ++ - group02 ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for host. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for host, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for host. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for host, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_host: ++ - "{{ host1_fqdn }}" ++ - "{{ host2_fqdn }}" ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for hostgroup. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for hostgroup, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for hostgroup. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for hostgroup, again. ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "HTTP/{{ svc_fqdn }}" ++ allow_retrieve_keytab_hostgroup: ++ - hostgroup01 ++ - hostgroup02 ++ action: member ++ state: absent ++ register: result ++ failed_when: result.changed ++ ++ # cleanup ++ - name: Clean-up envirnoment. ++ include_tasks: env_cleanup.yml +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-Fix-forwardzone-issues_rhbz#1843826,1843828,1843829,1843830,1843831.patch b/SOURCES/ansible-freeipa-0.1.12-Fix-forwardzone-issues_rhbz#1843826,1843828,1843829,1843830,1843831.patch new file mode 100644 index 0000000..5470d06 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Fix-forwardzone-issues_rhbz#1843826,1843828,1843829,1843830,1843831.patch @@ -0,0 +1,1447 @@ +From f0f933b4630bce810475a519e295828013d301d6 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Wed, 10 Jun 2020 20:40:45 -0300 +Subject: [PATCH] Changed admin password on tests to match other modules. + +Use of the same password on all module tests ease test automation, +and this change ensure that dnsforwardzone use the same password as +other modules. +--- + tests/dnsforwardzone/test_dnsforwardzone.yml | 42 ++++++++++---------- + 1 file changed, 21 insertions(+), 21 deletions(-) + +diff --git a/tests/dnsforwardzone/test_dnsforwardzone.yml b/tests/dnsforwardzone/test_dnsforwardzone.yml +index 1a45e826..ac08a48f 100644 +--- a/tests/dnsforwardzone/test_dnsforwardzone.yml ++++ b/tests/dnsforwardzone/test_dnsforwardzone.yml +@@ -7,13 +7,13 @@ + tasks: + - name: ensure forwardzone example.com is absent - prep + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: absent + + - name: ensure forwardzone example.com is created + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -25,7 +25,7 @@ + + - name: ensure forwardzone example.com is present again + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -37,7 +37,7 @@ + + - name: ensure forwardzone example.com has two forwarders + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -50,7 +50,7 @@ + + - name: ensure forwardzone example.com has one forwarder again + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + forwarders: + - 8.8.8.8 +@@ -62,7 +62,7 @@ + + - name: skip_overlap_check can only be set on creation so change nothing + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + forwarders: + - 8.8.8.8 +@@ -74,7 +74,7 @@ + + - name: change all the things at once + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -87,13 +87,13 @@ + + - name: ensure forwardzone example.com is absent for next testset + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: absent + + - name: ensure forwardzone example.com is created with minimal args + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + skip_overlap_check: true +@@ -104,7 +104,7 @@ + + - name: add a forwarder to any existing ones + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -115,7 +115,7 @@ + + - name: check the list of forwarders is what we expect + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -127,7 +127,7 @@ + + - name: remove a single forwarder + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: absent + name: example.com + forwarders: +@@ -138,7 +138,7 @@ + + - name: check the list of forwarders is what we expect now + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -149,13 +149,13 @@ + + - name: ensure forwardzone example.com is absent again + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: absent + + - name: try to create a new forwarder with action=member + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -167,13 +167,13 @@ + + - name: ensure forwardzone example.com is absent - tidy up + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: absent + + - name: try to create a new forwarder is disabled state + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: disabled + name: example.com + forwarders: +@@ -184,7 +184,7 @@ + + - name: enable the forwarder + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: enabled + register: result +@@ -192,7 +192,7 @@ + + - name: disable the forwarder again + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: disabled + action: member +@@ -201,7 +201,7 @@ + + - name: ensure it stays disabled + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: disabled + register: result +@@ -209,6 +209,6 @@ + + - name: ensure forwardzone example.com is absent - tidy up + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: absent +From f8ebca760dbaaf38c7b74b0c855b05d26e9cb812 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Wed, 10 Jun 2020 22:14:27 -0300 +Subject: [PATCH] Allow processing of multiple names for deleting + dnsforwardzones. + +--- + plugins/modules/ipadnsforwardzone.py | 189 ++++++++++++++------------- + 1 file changed, 98 insertions(+), 91 deletions(-) + +diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py +index 90bd3876..b28f28db 100644 +--- a/plugins/modules/ipadnsforwardzone.py ++++ b/plugins/modules/ipadnsforwardzone.py +@@ -134,7 +134,7 @@ def main(): + # general + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), +- name=dict(type="str", aliases=["cn"], default=None, ++ name=dict(type="list", aliases=["cn"], default=None, + required=True), + forwarders=dict(type='list', aliases=["idnsforwarders"], + required=False), +@@ -158,7 +158,7 @@ def main(): + "ipaadmin_principal") + ipaadmin_password = module_params_get(ansible_module, + "ipaadmin_password") +- name = module_params_get(ansible_module, "name") ++ names = module_params_get(ansible_module, "name") + action = module_params_get(ansible_module, "action") + forwarders = module_params_get(ansible_module, "forwarders") + forwardpolicy = module_params_get(ansible_module, "forwardpolicy") +@@ -166,6 +166,12 @@ def main(): + "skip_overlap_check") + state = module_params_get(ansible_module, "state") + ++ if state == 'present' and len(names) != 1: ++ ansible_module.fail_json( ++ msg="Only one dnsforwardzone can be added at a time.") ++ if state == 'absent' and len(names) < 1: ++ ansible_module.fail_json(msg="No name given.") ++ + # absent stae means delete if the action is NOT member but update if it is + # if action is member then update an exisiting resource + # and if action is not member then create a resource +@@ -207,101 +213,102 @@ def main(): + ipaadmin_password) + api_connect() + +- # Make sure forwardzone exists +- existing_resource = find_dnsforwardzone(ansible_module, name) +- +- if existing_resource is None and operation == "update": +- # does not exist and is updating +- # trying to update something that doesn't exist, so error +- ansible_module.fail_json(msg="""dnsforwardzone '%s' is not +- valid""" % (name)) +- elif existing_resource is None and operation == "del": +- # does not exists and should be absent +- # set command +- command = None +- # enabled or disabled? +- is_enabled = "IGNORE" +- elif existing_resource is not None and operation == "del": +- # exists but should be absent +- # set command +- command = "dnsforwardzone_del" +- # enabled or disabled? +- is_enabled = "IGNORE" +- elif forwarders is None: +- # forwarders are not defined its not a delete, update state? +- # set command +- command = None +- # enabled or disabled? +- if existing_resource is not None: +- is_enabled = existing_resource["idnszoneactive"][0] +- else: +- is_enabled = "IGNORE" +- elif existing_resource is not None and operation == "update": +- # exists and is updating +- # calculate the new forwarders and mod +- # determine args +- if state != "absent": +- forwarders = list(set(existing_resource["idnsforwarders"] +- + forwarders)) +- else: +- forwarders = list(set(existing_resource["idnsforwarders"]) +- - set(forwarders)) +- args = gen_args(forwarders, forwardpolicy, +- skip_overlap_check) +- if skip_overlap_check is not None: +- del args['skip_overlap_check'] +- +- # command +- if not compare_args_ipa(ansible_module, args, existing_resource): +- command = "dnsforwardzone_mod" +- else: ++ for name in names: ++ # Make sure forwardzone exists ++ existing_resource = find_dnsforwardzone(ansible_module, name) ++ ++ if existing_resource is None and operation == "update": ++ # does not exist and is updating ++ # trying to update something that doesn't exist, so error ++ ansible_module.fail_json(msg="""dnsforwardzone '%s' is not ++ valid""" % (name)) ++ elif existing_resource is None and operation == "del": ++ # does not exists and should be absent ++ # set command + command = None +- +- # enabled or disabled? +- is_enabled = existing_resource["idnszoneactive"][0] +- +- elif existing_resource is None and operation == "add": +- # does not exist but should be present +- # determine args +- args = gen_args(forwarders, forwardpolicy, +- skip_overlap_check) +- # set command +- command = "dnsforwardzone_add" +- # enabled or disabled? +- is_enabled = "TRUE" +- +- elif existing_resource is not None and operation == "add": +- # exists and should be present, has it changed? +- # determine args +- args = gen_args(forwarders, forwardpolicy, skip_overlap_check) +- if skip_overlap_check is not None: +- del args['skip_overlap_check'] +- +- # set command +- if not compare_args_ipa(ansible_module, args, existing_resource): +- command = "dnsforwardzone_mod" +- else: ++ # enabled or disabled? ++ is_enabled = "IGNORE" ++ elif existing_resource is not None and operation == "del": ++ # exists but should be absent ++ # set command ++ command = "dnsforwardzone_del" ++ # enabled or disabled? ++ is_enabled = "IGNORE" ++ elif forwarders is None: ++ # forwarders are not defined its not a delete, update state? ++ # set command + command = None ++ # enabled or disabled? ++ if existing_resource is not None: ++ is_enabled = existing_resource["idnszoneactive"][0] ++ else: ++ is_enabled = "IGNORE" ++ elif existing_resource is not None and operation == "update": ++ # exists and is updating ++ # calculate the new forwarders and mod ++ # determine args ++ if state != "absent": ++ forwarders = list(set(existing_resource["idnsforwarders"] ++ + forwarders)) ++ else: ++ forwarders = list(set(existing_resource["idnsforwarders"]) ++ - set(forwarders)) ++ args = gen_args(forwarders, forwardpolicy, ++ skip_overlap_check) ++ if skip_overlap_check is not None: ++ del args['skip_overlap_check'] ++ ++ # command ++ if not compare_args_ipa(ansible_module, args, existing_resource): ++ command = "dnsforwardzone_mod" ++ else: ++ command = None ++ ++ # enabled or disabled? ++ is_enabled = existing_resource["idnszoneactive"][0] + +- # enabled or disabled? +- is_enabled = existing_resource["idnszoneactive"][0] +- +- # if command is set then run it with the args +- if command is not None: +- api_command(ansible_module, command, name, args) +- changed = True ++ elif existing_resource is None and operation == "add": ++ # does not exist but should be present ++ # determine args ++ args = gen_args(forwarders, forwardpolicy, ++ skip_overlap_check) ++ # set command ++ command = "dnsforwardzone_add" ++ # enabled or disabled? ++ is_enabled = "TRUE" ++ ++ elif existing_resource is not None and operation == "add": ++ # exists and should be present, has it changed? ++ # determine args ++ args = gen_args(forwarders, forwardpolicy, skip_overlap_check) ++ if skip_overlap_check is not None: ++ del args['skip_overlap_check'] ++ ++ # set command ++ if not compare_args_ipa(ansible_module, args, existing_resource): ++ command = "dnsforwardzone_mod" ++ else: ++ command = None ++ ++ # enabled or disabled? ++ is_enabled = existing_resource["idnszoneactive"][0] + +- # does the enabled state match what we want (if we care) +- if is_enabled != "IGNORE": +- if wants_enable and is_enabled != "TRUE": +- api_command(ansible_module, "dnsforwardzone_enable", +- name, {}) +- changed = True +- elif not wants_enable and is_enabled != "FALSE": +- api_command(ansible_module, "dnsforwardzone_disable", +- name, {}) ++ # if command is set then run it with the args ++ if command is not None: ++ api_command(ansible_module, command, name, args) + changed = True + ++ # does the enabled state match what we want (if we care) ++ if is_enabled != "IGNORE": ++ if wants_enable and is_enabled != "TRUE": ++ api_command(ansible_module, "dnsforwardzone_enable", ++ name, {}) ++ changed = True ++ elif not wants_enable and is_enabled != "FALSE": ++ api_command(ansible_module, "dnsforwardzone_disable", ++ name, {}) ++ changed = True ++ + except Exception as e: + ansible_module.fail_json(msg=str(e)) + +From 3f785bc0e9fe1ab3ad874ce4f26e6897189db8aa Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Wed, 10 Jun 2020 22:20:20 -0300 +Subject: [PATCH] Fix error message when adding dnsforwardzone without + forwarders. + +--- + plugins/modules/ipadnsforwardzone.py | 5 +++++ + tests/dnsforwardzone/test_dnsforwardzone.yml | 13 +++++++++++-- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py +index b28f28db..3968e6a1 100644 +--- a/plugins/modules/ipadnsforwardzone.py ++++ b/plugins/modules/ipadnsforwardzone.py +@@ -217,6 +217,11 @@ def main(): + # Make sure forwardzone exists + existing_resource = find_dnsforwardzone(ansible_module, name) + ++ # validate parameters ++ if state == 'present': ++ if existing_resource is None and not forwarders: ++ ansible_module.fail_json(msg='No forwarders specified.') ++ + if existing_resource is None and operation == "update": + # does not exist and is updating + # trying to update something that doesn't exist, so error +diff --git a/tests/dnsforwardzone/test_dnsforwardzone.yml b/tests/dnsforwardzone/test_dnsforwardzone.yml +index ac08a48f..d94db9e5 100644 +--- a/tests/dnsforwardzone/test_dnsforwardzone.yml ++++ b/tests/dnsforwardzone/test_dnsforwardzone.yml +@@ -5,10 +5,12 @@ + gather_facts: false + + tasks: +- - name: ensure forwardzone example.com is absent - prep ++ - name: ensure test forwardzones are absent - prep + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +- name: example.com ++ name: ++ - example.com ++ - newfailzone.com + state: absent + + - name: ensure forwardzone example.com is created +@@ -207,6 +209,13 @@ + register: result + failed_when: result.changed + ++ - name: Ensure forwardzone is not added without forwarders, with correct message. ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: newfailzone.com ++ register: result ++ failed_when: not result.failed or "No forwarders specified" not in result.msg ++ + - name: ensure forwardzone example.com is absent - tidy up + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +From 1d223c2b63634abe86f7702a64dd83c4fbc272ce Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Mon, 15 Jun 2020 16:14:25 -0300 +Subject: [PATCH] Add support for attributes `ip_address` and `port` to + `forwarders`. + +This patch modify the was forwarders are configured, using two attributes, +`ip_address` and `port`, instead of IPA API internal string representation +of `IP port PORT`. +--- + README-dnsforwardzone.md | 6 ++- + plugins/modules/ipadnsforwardzone.py | 37 ++++++++++++++--- + tests/dnsforwardzone/test_dnsforwardzone.yml | 43 ++++++++++++-------- + 3 files changed, 62 insertions(+), 24 deletions(-) + +diff --git a/README-dnsforwardzone.md b/README-dnsforwardzone.md +index 81919295..15b2b574 100644 +--- a/README-dnsforwardzone.md ++++ b/README-dnsforwardzone.md +@@ -99,8 +99,10 @@ 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 + `name` \| `cn` | Zone name (FQDN). | yes if `state` == `present` +-`forwarders` \| `idnsforwarders` | Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`) | no +-`forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no ++`forwarders` \| `idnsforwarders` | Per-zone forwarders. A custom port can be specified for each forwarder. Options | no ++  | `ip_address`: The forwarder IP address. | yes ++  | `port`: The forwarder IP port. | no ++`forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no + `skip_overlap_check` | Force DNS zone creation even if it will overlap with an existing zone. Defaults to False. | no + `action` | Work on group or member level. It can be on of `member` or `dnsforwardzone` and defaults to `dnsforwardzone`. | no + `state` | The state to ensure. It can be one of `present`, `absent`, `enabled` or `disabled`, default: `present`. | yes +diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py +index 3968e6a1..8e5c3464 100644 +--- a/plugins/modules/ipadnsforwardzone.py ++++ b/plugins/modules/ipadnsforwardzone.py +@@ -54,9 +54,16 @@ + forwarders: + description: + - List of the DNS servers to forward to +- required: true +- type: list + aliases: ["idnsforwarders"] ++ options: ++ ip_address: ++ description: Forwarder IP address (either IPv4 or IPv6). ++ required: false ++ type: string ++ port: ++ description: Forwarder port. ++ required: false ++ type: int + forwardpolicy: + description: Per-zone conditional forwarding policy + required: false +@@ -128,6 +135,20 @@ def gen_args(forwarders, forwardpolicy, skip_overlap_check): + return _args + + ++def forwarder_list(forwarders): ++ """Convert the forwarder dict into a list compatible with IPA API.""" ++ if forwarders is None: ++ return None ++ fwd_list = [] ++ for forwarder in forwarders: ++ if forwarder.get('port', None) is not None: ++ formatter = "{ip_address} port {port}" ++ else: ++ formatter = "{ip_address}" ++ fwd_list.append(formatter.format(**forwarder)) ++ return fwd_list ++ ++ + def main(): + ansible_module = AnsibleModule( + argument_spec=dict( +@@ -136,8 +157,13 @@ def main(): + ipaadmin_password=dict(type="str", required=False, no_log=True), + name=dict(type="list", aliases=["cn"], default=None, + required=True), +- forwarders=dict(type='list', aliases=["idnsforwarders"], +- required=False), ++ forwarders=dict(type="list", default=None, required=False, ++ aliases=["idnsforwarders"], elements='dict', ++ options=dict( ++ ip_address=dict(type='str', required=True), ++ port=dict(type='int', required=False, ++ default=None), ++ )), + forwardpolicy=dict(type='str', aliases=["idnsforwardpolicy"], + required=False, + choices=['only', 'first', 'none']), +@@ -160,7 +186,8 @@ def main(): + "ipaadmin_password") + names = module_params_get(ansible_module, "name") + action = module_params_get(ansible_module, "action") +- forwarders = module_params_get(ansible_module, "forwarders") ++ forwarders = forwarder_list( ++ module_params_get(ansible_module, "forwarders")) + forwardpolicy = module_params_get(ansible_module, "forwardpolicy") + skip_overlap_check = module_params_get(ansible_module, + "skip_overlap_check") +diff --git a/tests/dnsforwardzone/test_dnsforwardzone.yml b/tests/dnsforwardzone/test_dnsforwardzone.yml +index d94db9e5..468cd4ce 100644 +--- a/tests/dnsforwardzone/test_dnsforwardzone.yml ++++ b/tests/dnsforwardzone/test_dnsforwardzone.yml +@@ -5,7 +5,7 @@ + gather_facts: false + + tasks: +- - name: ensure test forwardzones are absent - prep ++ - name: ensure test forwardzones are absent + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: +@@ -19,7 +19,7 @@ + state: present + name: example.com + forwarders: +- - 8.8.8.8 ++ - ip_address: 8.8.8.8 + forwardpolicy: first + skip_overlap_check: true + register: result +@@ -31,7 +31,7 @@ + state: present + name: example.com + forwarders: +- - 8.8.8.8 ++ - ip_address: 8.8.8.8 + forwardpolicy: first + skip_overlap_check: true + register: result +@@ -43,19 +43,22 @@ + state: present + name: example.com + forwarders: +- - 8.8.8.8 +- - 4.4.4.4 ++ - ip_address: 8.8.8.8 ++ - ip_address: 4.4.4.4 ++ port: 8053 + forwardpolicy: first + skip_overlap_check: true + register: result + failed_when: not result.changed + ++ - pause: ++ + - name: ensure forwardzone example.com has one forwarder again + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: example.com + forwarders: +- - 8.8.8.8 ++ - ip_address: 8.8.8.8 + forwardpolicy: first + skip_overlap_check: true + state: present +@@ -67,7 +70,7 @@ + ipaadmin_password: SomeADMINpassword + name: example.com + forwarders: +- - 8.8.8.8 ++ - ip_address: 8.8.8.8 + forwardpolicy: first + skip_overlap_check: false + state: present +@@ -80,8 +83,9 @@ + state: present + name: example.com + forwarders: +- - 8.8.8.8 +- - 4.4.4.4 ++ - ip_address: 8.8.8.8 ++ - ip_address: 4.4.4.4 ++ port: 8053 + forwardpolicy: only + skip_overlap_check: false + register: result +@@ -100,7 +104,7 @@ + name: example.com + skip_overlap_check: true + forwarders: +- - 8.8.8.8 ++ - ip_address: 8.8.8.8 + register: result + failed_when: not result.changed + +@@ -110,7 +114,8 @@ + state: present + name: example.com + forwarders: +- - 4.4.4.4 ++ - ip_address: 4.4.4.4 ++ port: 8053 + action: member + register: result + failed_when: not result.changed +@@ -121,8 +126,9 @@ + state: present + name: example.com + forwarders: +- - 4.4.4.4 +- - 8.8.8.8 ++ - ip_address: 4.4.4.4 ++ port: 8053 ++ - ip_address: 8.8.8.8 + action: member + register: result + failed_when: result.changed +@@ -133,7 +139,7 @@ + state: absent + name: example.com + forwarders: +- - 8.8.8.8 ++ - ip_address: 8.8.8.8 + action: member + register: result + failed_when: not result.changed +@@ -144,7 +150,8 @@ + state: present + name: example.com + forwarders: +- - 4.4.4.4 ++ - ip_address: 4.4.4.4 ++ port: 8053 + action: member + register: result + failed_when: result.changed +@@ -161,7 +168,8 @@ + state: present + name: example.com + forwarders: +- - 4.4.4.4 ++ - ip_address: 4.4.4.4 ++ port: 8053 + action: member + skip_overlap_check: true + register: result +@@ -179,7 +187,8 @@ + state: disabled + name: example.com + forwarders: +- - 4.4.4.4 ++ - ip_address: 4.4.4.4 ++ port: 8053 + skip_overlap_check: true + register: result + failed_when: not result.changed +From bf864469a1da81c6b23e9726562b21408764ac8f Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Mon, 15 Jun 2020 20:42:23 -0300 +Subject: [PATCH] Add support for attribute `permission` on dnsforwardzone + module. + +Adds missing attribute `permission to dnsforwardzone module, that +enable setting `manageby` for the DNS Forwar Zone. +--- + README-dnsforwardzone.md | 1 + + plugins/modules/ipadnsforwardzone.py | 71 ++++++++---- + tests/dnsforwardzone/test_dnsforwardzone.yml | 110 +++++++++++++++---- + 3 files changed, 136 insertions(+), 46 deletions(-) + +diff --git a/README-dnsforwardzone.md b/README-dnsforwardzone.md +index 15b2b574..175e6f8b 100644 +--- a/README-dnsforwardzone.md ++++ b/README-dnsforwardzone.md +@@ -104,6 +104,7 @@ Variable | Description | Required +   | `port`: The forwarder IP port. | no + `forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no + `skip_overlap_check` | Force DNS zone creation even if it will overlap with an existing zone. Defaults to False. | no ++`permission` | Allow DNS Forward Zone to be managed. (bool) | no + `action` | Work on group or member level. It can be on of `member` or `dnsforwardzone` and defaults to `dnsforwardzone`. | no + `state` | The state to ensure. It can be one of `present`, `absent`, `enabled` or `disabled`, default: `present`. | yes + +diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py +index 8e5c3464..a729197b 100644 +--- a/plugins/modules/ipadnsforwardzone.py ++++ b/plugins/modules/ipadnsforwardzone.py +@@ -75,6 +75,11 @@ + - Force DNS zone creation even if it will overlap with an existing zone. + required: false + default: false ++ permission: ++ description: ++ - Allow DNS Forward Zone to be managed. ++ required: false ++ type: bool + ''' + + EXAMPLES = ''' +@@ -168,6 +173,8 @@ def main(): + required=False, + choices=['only', 'first', 'none']), + skip_overlap_check=dict(type='bool', required=False), ++ permission=dict(type='bool', required=False, ++ aliases=['managedby']), + action=dict(type="str", default="dnsforwardzone", + choices=["member", "dnsforwardzone"]), + # state +@@ -191,6 +198,7 @@ def main(): + forwardpolicy = module_params_get(ansible_module, "forwardpolicy") + skip_overlap_check = module_params_get(ansible_module, + "skip_overlap_check") ++ permission = module_params_get(ansible_module, "permission") + state = module_params_get(ansible_module, "state") + + if state == 'present' and len(names) != 1: +@@ -215,7 +223,9 @@ def main(): + wants_enable = True + + if operation == "del": +- invalid = ["forwarders", "forwardpolicy", "skip_overlap_check"] ++ invalid = [ ++ "forwarders", "forwardpolicy", "skip_overlap_check", "permission" ++ ] + for x in invalid: + if vars()[x] is not None: + ansible_module.fail_json( +@@ -241,6 +251,9 @@ def main(): + api_connect() + + for name in names: ++ commands = [] ++ command = None ++ + # Make sure forwardzone exists + existing_resource = find_dnsforwardzone(ansible_module, name) + +@@ -249,6 +262,18 @@ def main(): + if existing_resource is None and not forwarders: + ansible_module.fail_json(msg='No forwarders specified.') + ++ if existing_resource is not None: ++ if state != "absent": ++ if forwarders: ++ forwarders = list( ++ set(existing_resource["idnsforwarders"] ++ + forwarders)) ++ else: ++ if forwarders: ++ forwarders = list( ++ set(existing_resource["idnsforwarders"]) ++ - set(forwarders)) ++ + if existing_resource is None and operation == "update": + # does not exist and is updating + # trying to update something that doesn't exist, so error +@@ -256,20 +281,17 @@ def main(): + valid""" % (name)) + elif existing_resource is None and operation == "del": + # does not exists and should be absent +- # set command +- command = None + # enabled or disabled? + is_enabled = "IGNORE" + elif existing_resource is not None and operation == "del": + # exists but should be absent + # set command + command = "dnsforwardzone_del" ++ args = {} + # enabled or disabled? + is_enabled = "IGNORE" + elif forwarders is None: + # forwarders are not defined its not a delete, update state? +- # set command +- command = None + # enabled or disabled? + if existing_resource is not None: + is_enabled = existing_resource["idnszoneactive"][0] +@@ -278,23 +300,13 @@ def main(): + elif existing_resource is not None and operation == "update": + # exists and is updating + # calculate the new forwarders and mod +- # determine args +- if state != "absent": +- forwarders = list(set(existing_resource["idnsforwarders"] +- + forwarders)) +- else: +- forwarders = list(set(existing_resource["idnsforwarders"]) +- - set(forwarders)) +- args = gen_args(forwarders, forwardpolicy, +- skip_overlap_check) +- if skip_overlap_check is not None: ++ args = gen_args(forwarders, forwardpolicy, skip_overlap_check) ++ if "skip_overlap_check" in args: + del args['skip_overlap_check'] + + # command + if not compare_args_ipa(ansible_module, args, existing_resource): + command = "dnsforwardzone_mod" +- else: +- command = None + + # enabled or disabled? + is_enabled = existing_resource["idnszoneactive"][0] +@@ -313,21 +325,36 @@ def main(): + # exists and should be present, has it changed? + # determine args + args = gen_args(forwarders, forwardpolicy, skip_overlap_check) +- if skip_overlap_check is not None: ++ if 'skip_overlap_check' in args: + del args['skip_overlap_check'] + + # set command + if not compare_args_ipa(ansible_module, args, existing_resource): + command = "dnsforwardzone_mod" +- else: +- command = None + + # enabled or disabled? + is_enabled = existing_resource["idnszoneactive"][0] + +- # if command is set then run it with the args ++ # if command is set... + if command is not None: +- api_command(ansible_module, command, name, args) ++ commands.append([name, command, args]) ++ ++ if permission is not None: ++ if existing_resource is None: ++ managedby = None ++ else: ++ managedby = existing_resource.get('managedby', None) ++ if permission and managedby is None: ++ commands.append( ++ [name, 'dnsforwardzone_add_permission', {}] ++ ) ++ elif not permission and managedby is not None: ++ commands.append( ++ [name, 'dnsforwardzone_remove_permission', {}] ++ ) ++ ++ for name, command, args in commands: ++ result = api_command(ansible_module, command, name, args) + changed = True + + # does the enabled state match what we want (if we care) +diff --git a/tests/dnsforwardzone/test_dnsforwardzone.yml b/tests/dnsforwardzone/test_dnsforwardzone.yml +index 468cd4ce..0386bd48 100644 +--- a/tests/dnsforwardzone/test_dnsforwardzone.yml ++++ b/tests/dnsforwardzone/test_dnsforwardzone.yml +@@ -51,8 +51,6 @@ + register: result + failed_when: not result.changed + +- - pause: +- + - name: ensure forwardzone example.com has one forwarder again + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +@@ -63,7 +61,7 @@ + skip_overlap_check: true + state: present + register: result +- failed_when: not result.changed ++ failed_when: result.changed + + - name: skip_overlap_check can only be set on creation so change nothing + ipadnsforwardzone: +@@ -77,6 +75,22 @@ + register: result + failed_when: result.changed + ++ - name: ensure forwardzone example.com is absent. ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ state: absent ++ register: result ++ failed_when: not result.changed ++ ++ - name: ensure forwardzone example.com is absent, again. ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ state: absent ++ register: result ++ failed_when: result.changed ++ + - name: change all the things at once + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +@@ -87,11 +101,12 @@ + - ip_address: 4.4.4.4 + port: 8053 + forwardpolicy: only +- skip_overlap_check: false ++ skip_overlap_check: true ++ permission: yes + register: result + failed_when: not result.changed + +- - name: ensure forwardzone example.com is absent for next testset ++ - name: ensure forwardzone example.com is absent. + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: example.com +@@ -156,43 +171,58 @@ + register: result + failed_when: result.changed + +- - name: ensure forwardzone example.com is absent again ++ - name: Add a permission for per-forward zone access delegation. + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: example.com +- state: absent ++ permission: yes ++ action: member ++ register: result ++ failed_when: not result.changed + +- - name: try to create a new forwarder with action=member ++ - name: Add a permission for per-forward zone access delegation, again. + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +- state: present + name: example.com +- forwarders: +- - ip_address: 4.4.4.4 +- port: 8053 ++ permission: yes + action: member +- skip_overlap_check: true + register: result + failed_when: result.changed + +- - name: ensure forwardzone example.com is absent - tidy up ++ - name: Remove a permission for per-forward zone access delegation. + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: example.com +- state: absent ++ permission: no ++ action: member ++ register: result ++ failed_when: not result.changed + +- - name: try to create a new forwarder is disabled state ++ - name: Remove a permission for per-forward zone access delegation, again. + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +- state: disabled + name: example.com +- forwarders: +- - ip_address: 4.4.4.4 +- port: 8053 +- skip_overlap_check: true ++ permission: no ++ action: member ++ register: result ++ failed_when: result.changed ++ ++ - name: disable the forwarder ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ state: disabled + register: result + failed_when: not result.changed + ++ - name: disable the forwarder again ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ state: disabled ++ register: result ++ failed_when: result.changed ++ + - name: enable the forwarder + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +@@ -201,12 +231,42 @@ + register: result + failed_when: not result.changed + +- - name: disable the forwarder again ++ - name: enable the forwarder, again + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: example.com +- state: disabled ++ state: enabled ++ register: result ++ failed_when: result.changed ++ ++ - name: ensure forwardzone example.com is absent again ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ state: absent ++ ++ - name: try to create a new forwarder with action=member ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ state: present ++ name: example.com ++ forwarders: ++ - ip_address: 4.4.4.4 ++ port: 8053 + action: member ++ skip_overlap_check: true ++ register: result ++ failed_when: result.changed ++ ++ - name: try to create a new forwarder with disabled state ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ state: disabled ++ name: example.com ++ forwarders: ++ - ip_address: 4.4.4.4 ++ port: 8053 ++ skip_overlap_check: yes + register: result + failed_when: not result.changed + +@@ -228,5 +288,7 @@ + - name: ensure forwardzone example.com is absent - tidy up + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +- name: example.com ++ name: ++ - example.com ++ - newfailzone.com + state: absent +From 857fb82eb9141a44ffb91331653e1c30b43f671e Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Mon, 15 Jun 2020 23:40:35 -0300 +Subject: [PATCH] Allows modification of forward policy in existing DNS Forward + Zone. + +This patch allows the modification of the forward zone policy in +an existing DNS Forward Zone, and fixes some issues with `enable` +and `disable` state that prevented correct behavior of `forwardpolicy`. +--- + plugins/modules/ipadnsforwardzone.py | 154 ++++++++++--------- + tests/dnsforwardzone/test_dnsforwardzone.yml | 32 ++-- + 2 files changed, 97 insertions(+), 89 deletions(-) + +diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py +index a729197b..1f1e85ec 100644 +--- a/plugins/modules/ipadnsforwardzone.py ++++ b/plugins/modules/ipadnsforwardzone.py +@@ -217,10 +217,20 @@ def main(): + else: + operation = "add" + +- if state == "disabled": +- wants_enable = False +- else: +- wants_enable = True ++ if state in ["enabled", "disabled"]: ++ if action == "member": ++ ansible_module.fail_json( ++ msg="Action `member` cannot be used with state `%s`" ++ % (state)) ++ invalid = [ ++ "forwarders", "forwardpolicy", "skip_overlap_check", "permission" ++ ] ++ for x in invalid: ++ if vars()[x] is not None: ++ ansible_module.fail_json( ++ msg="Argument '%s' can not be used with action " ++ "'%s', state `%s`" % (x, action, state)) ++ wants_enable = (state == "enabled") + + if operation == "del": + invalid = [ +@@ -230,7 +240,7 @@ def main(): + if vars()[x] is not None: + ansible_module.fail_json( + msg="Argument '%s' can not be used with action " +- "'%s'" % (x, action)) ++ "'%s', state `%s`" % (x, action, state)) + + changed = False + exit_args = {} +@@ -262,7 +272,27 @@ def main(): + if existing_resource is None and not forwarders: + ansible_module.fail_json(msg='No forwarders specified.') + +- if existing_resource is not None: ++ if existing_resource is None: ++ if operation == "add": ++ # does not exist but should be present ++ # determine args ++ args = gen_args(forwarders, forwardpolicy, ++ skip_overlap_check) ++ # set command ++ command = "dnsforwardzone_add" ++ # enabled or disabled? ++ ++ elif operation == "update": ++ # does not exist and is updating ++ # trying to update something that doesn't exist, so error ++ ansible_module.fail_json( ++ msg="dnsforwardzone '%s' not found." % (name)) ++ ++ elif operation == "del": ++ # there's nothnig to do. ++ continue ++ ++ else: # existing_resource is not None + if state != "absent": + if forwarders: + forwarders = list( +@@ -274,66 +304,51 @@ def main(): + set(existing_resource["idnsforwarders"]) + - set(forwarders)) + +- if existing_resource is None and operation == "update": +- # does not exist and is updating +- # trying to update something that doesn't exist, so error +- ansible_module.fail_json(msg="""dnsforwardzone '%s' is not +- valid""" % (name)) +- elif existing_resource is None and operation == "del": +- # does not exists and should be absent +- # enabled or disabled? +- is_enabled = "IGNORE" +- elif existing_resource is not None and operation == "del": +- # exists but should be absent +- # set command +- command = "dnsforwardzone_del" +- args = {} +- # enabled or disabled? +- is_enabled = "IGNORE" +- elif forwarders is None: +- # forwarders are not defined its not a delete, update state? +- # enabled or disabled? ++ if operation == "add": ++ # exists and should be present, has it changed? ++ # determine args ++ args = gen_args( ++ forwarders, forwardpolicy, skip_overlap_check) ++ if 'skip_overlap_check' in args: ++ del args['skip_overlap_check'] ++ ++ # set command ++ if not compare_args_ipa( ++ ansible_module, args, existing_resource): ++ command = "dnsforwardzone_mod" ++ ++ elif operation == "del": ++ # exists but should be absent ++ # set command ++ command = "dnsforwardzone_del" ++ args = {} ++ ++ elif operation == "update": ++ # exists and is updating ++ # calculate the new forwarders and mod ++ args = gen_args( ++ forwarders, forwardpolicy, skip_overlap_check) ++ if "skip_overlap_check" in args: ++ del args['skip_overlap_check'] ++ ++ # command ++ if not compare_args_ipa( ++ ansible_module, args, existing_resource): ++ command = "dnsforwardzone_mod" ++ ++ if state in ['enabled', 'disabled']: + if existing_resource is not None: + is_enabled = existing_resource["idnszoneactive"][0] + else: +- is_enabled = "IGNORE" +- elif existing_resource is not None and operation == "update": +- # exists and is updating +- # calculate the new forwarders and mod +- args = gen_args(forwarders, forwardpolicy, skip_overlap_check) +- if "skip_overlap_check" in args: +- del args['skip_overlap_check'] +- +- # command +- if not compare_args_ipa(ansible_module, args, existing_resource): +- command = "dnsforwardzone_mod" +- +- # enabled or disabled? +- is_enabled = existing_resource["idnszoneactive"][0] +- +- elif existing_resource is None and operation == "add": +- # does not exist but should be present +- # determine args +- args = gen_args(forwarders, forwardpolicy, +- skip_overlap_check) +- # set command +- command = "dnsforwardzone_add" +- # enabled or disabled? +- is_enabled = "TRUE" +- +- elif existing_resource is not None and operation == "add": +- # exists and should be present, has it changed? +- # determine args +- args = gen_args(forwarders, forwardpolicy, skip_overlap_check) +- if 'skip_overlap_check' in args: +- del args['skip_overlap_check'] +- +- # set command +- if not compare_args_ipa(ansible_module, args, existing_resource): +- command = "dnsforwardzone_mod" +- +- # enabled or disabled? +- is_enabled = existing_resource["idnszoneactive"][0] ++ ansible_module.fail_json( ++ msg="dnsforwardzone '%s' not found." % (name)) ++ ++ # does the enabled state match what we want (if we care) ++ if is_enabled != "IGNORE": ++ if wants_enable and is_enabled != "TRUE": ++ commands.append([name, "dnsforwardzone_enable", {}]) ++ elif not wants_enable and is_enabled != "FALSE": ++ commands.append([name, "dnsforwardzone_disable", {}]) + + # if command is set... + if command is not None: +@@ -354,20 +369,9 @@ def main(): + ) + + for name, command, args in commands: +- result = api_command(ansible_module, command, name, args) ++ api_command(ansible_module, command, name, args) + changed = True + +- # does the enabled state match what we want (if we care) +- if is_enabled != "IGNORE": +- if wants_enable and is_enabled != "TRUE": +- api_command(ansible_module, "dnsforwardzone_enable", +- name, {}) +- changed = True +- elif not wants_enable and is_enabled != "FALSE": +- api_command(ansible_module, "dnsforwardzone_disable", +- name, {}) +- changed = True +- + except Exception as e: + ansible_module.fail_json(msg=str(e)) + +diff --git a/tests/dnsforwardzone/test_dnsforwardzone.yml b/tests/dnsforwardzone/test_dnsforwardzone.yml +index 0386bd48..223cf3d0 100644 +--- a/tests/dnsforwardzone/test_dnsforwardzone.yml ++++ b/tests/dnsforwardzone/test_dnsforwardzone.yml +@@ -106,6 +106,22 @@ + register: result + failed_when: not result.changed + ++ - name: change zone forward policy ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ forwardpolicy: first ++ register: result ++ failed_when: not result.changed ++ ++ - name: change zone forward policy, again ++ ipadnsforwardzone: ++ ipaadmin_password: SomeADMINpassword ++ name: example.com ++ forwardpolicy: first ++ register: result ++ failed_when: result.changed ++ + - name: ensure forwardzone example.com is absent. + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword +@@ -256,27 +272,15 @@ + action: member + skip_overlap_check: true + register: result +- failed_when: result.changed ++ failed_when: not result.failed or "not found" not in result.msg + + - name: try to create a new forwarder with disabled state +- ipadnsforwardzone: +- ipaadmin_password: SomeADMINpassword +- state: disabled +- name: example.com +- forwarders: +- - ip_address: 4.4.4.4 +- port: 8053 +- skip_overlap_check: yes +- register: result +- failed_when: not result.changed +- +- - name: ensure it stays disabled + ipadnsforwardzone: + ipaadmin_password: SomeADMINpassword + name: example.com + state: disabled + register: result +- failed_when: result.changed ++ failed_when: not result.failed or "not found" not in result.msg + + - name: Ensure forwardzone is not added without forwarders, with correct message. + ipadnsforwardzone: +From 8da6a6937919d0c390b870113fb557649c39c815 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 26 Jun 2020 11:28:15 -0300 +Subject: [PATCH] Change password values in README to keep consistency with + other modules. + +--- + README-dnsforwardzone.md | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/README-dnsforwardzone.md b/README-dnsforwardzone.md +index 175e6f8b..32de7bfe 100644 +--- a/README-dnsforwardzone.md ++++ b/README-dnsforwardzone.md +@@ -49,7 +49,7 @@ Example playbook to ensure presence of a forwardzone to ipa DNS: + tasks: + - name: ensure presence of forwardzone for DNS requests for example.com to 8.8.8.8 + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -59,13 +59,13 @@ Example playbook to ensure presence of a forwardzone to ipa DNS: + + - name: ensure the forward zone is disabled + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: disabled + + - name: ensure presence of multiple upstream DNS servers for example.com + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -74,7 +74,7 @@ Example playbook to ensure presence of a forwardzone to ipa DNS: + + - name: ensure presence of another forwarder to any existing ones for example.com + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + state: present + name: example.com + forwarders: +@@ -83,7 +83,7 @@ Example playbook to ensure presence of a forwardzone to ipa DNS: + + - name: ensure the forwarder for example.com does not exists (delete it if needed) + ipadnsforwardzone: +- ipaadmin_password: password01 ++ ipaadmin_password: SomeADMINpassword + name: example.com + state: absent + ``` diff --git a/SOURCES/ansible-freeipa-0.1.12-Fixed-error-msgs-on-FreeIPABaseModule-subclasses_rhbz#1845051.patch b/SOURCES/ansible-freeipa-0.1.12-Fixed-error-msgs-on-FreeIPABaseModule-subclasses_rhbz#1845051.patch new file mode 100644 index 0000000..3e07764 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Fixed-error-msgs-on-FreeIPABaseModule-subclasses_rhbz#1845051.patch @@ -0,0 +1,40 @@ +From 563a03d94bfc29799ea964dab61a1ba35818b9bb Mon Sep 17 00:00:00 2001 +From: Sergio Oliveira Campos +Date: Thu, 30 Jul 2020 09:50:24 -0300 +Subject: [PATCH] Fixed error msgs on FreeIPABaseModule subclasses + +When a fail_json is called a SystemExit exeception is raised. +Since the FreeIPABaseModule has an internal context manager to deal +with exceptions this ContextManager captures the SystemExit. After +dealing destroying the kinit session the SystemExit must be raised again +to allow the fail_json to work properly. +--- + plugins/module_utils/ansible_freeipa_module.py | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py +index 122ea2e..a59e6e2 100644 +--- a/plugins/module_utils/ansible_freeipa_module.py ++++ b/plugins/module_utils/ansible_freeipa_module.py +@@ -610,12 +610,15 @@ class FreeIPABaseModule(AnsibleModule): + exit the module with proper arguments. + + """ +- if exc_val: +- self.fail_json(msg=str(exc_val)) +- + # TODO: shouldn't we also disconnect from api backend? + temp_kdestroy(self.ccache_dir, self.ccache_name) + ++ if exc_type == SystemExit: ++ raise ++ ++ if exc_val: ++ self.fail_json(msg=str(exc_val)) ++ + self.exit_json(changed=self.changed, user=self.exit_args) + + def get_command_errors(self, command, result): +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-Fixes-password-behavior-on-Vault-module_rhbz#1839200.patch b/SOURCES/ansible-freeipa-0.1.12-Fixes-password-behavior-on-Vault-module_rhbz#1839200.patch new file mode 100644 index 0000000..bd9e951 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Fixes-password-behavior-on-Vault-module_rhbz#1839200.patch @@ -0,0 +1,327 @@ +From 3e5c54d4fdb10deda9b7e4deaf2c537b132711c9 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 31 Jul 2020 11:30:51 -0300 +Subject: [PATCH] Fix identification of existing vault type. + +In some scenarios, the value of the vault type is returned as a tuple, +rather than a string, this made some changes to existing vault to fail. +With this change, the vault type is correctly retrieved, if it was not +provided by the user. +--- + plugins/modules/ipavault.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index 6a3c73e..8562ff7 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -494,8 +494,10 @@ def check_encryption_params(module, state, action, vault_type, salt, + new_password, new_password_file, res_find): + vault_type_invalid = [] + +- if res_find is not None: ++ if vault_type is None and res_find is not None: + vault_type = res_find['ipavaulttype'] ++ if isinstance(vault_type, (tuple, list)): ++ vault_type = vault_type[0] + + if vault_type == "standard": + vault_type_invalid = ['public_key', 'public_key_file', 'password', +-- +2.26.2 + +From d52364bac923f2935b948882d5825e7488b0e9cf Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 31 Jul 2020 11:32:36 -0300 +Subject: [PATCH] Fix random salt generation. + +The generation of a random salt, when one was not provided, was in the +wrong place and being generated too late to be used properly. Also, the +generation of the value was duplicated. +--- + plugins/modules/ipavault.py | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index 8562ff7..dffd972 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -768,7 +768,12 @@ def main(): + commands.append([name, "vault_mod_internal", args]) + + else: ++ if vault_type == 'symmetric' \ ++ and 'ipavaultsalt' not in args: ++ args['ipavaultsalt'] = os.urandom(32) ++ + commands.append([name, "vault_add_internal", args]) ++ + if vault_type != 'standard' and vault_data is None: + vault_data = '' + +@@ -826,14 +831,6 @@ def main(): + commands.append( + [name, 'vault_remove_owner', owner_del_args]) + +- if vault_type == 'symmetric' \ +- and 'ipavaultsalt' not in args: +- args['ipavaultsalt'] = os.urandom(32) +- +- if vault_type == 'symmetric' \ +- and 'ipavaultsalt' not in args: +- args['ipavaultsalt'] = os.urandom(32) +- + elif action in "member": + # Add users and groups + if any([users, groups, services]): +-- +2.26.2 + +From daee6a6c744a740329ca231a277229567619e10c Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 31 Jul 2020 11:33:47 -0300 +Subject: [PATCH] Fix verification of parameters for modifying `salt` + attribute. + +When modifying an existing vault to change the value of `salt`, the +password must also change. It is fine to "change" the password to the +same value, thus only changing the salt value. +--- + plugins/modules/ipavault.py | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index dffd972..a608e64 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -517,6 +517,16 @@ def check_encryption_params(module, state, action, vault_type, salt, + module.fail_json( + msg="Cannot modify password of inexistent vault.") + ++ if ( ++ salt is not None ++ and not( ++ any([password, password_file]) ++ and any([new_password, new_password_file]) ++ ) ++ ): ++ module.fail_json( ++ msg="Vault `salt` can only change when changing the password.") ++ + if vault_type == "asymmetric": + vault_type_invalid = [ + 'password', 'password_file', 'new_password', 'new_password_file' +-- +2.26.2 + +From 4ef4e706b79fdbb43e462b1a7130fc2cad5894b2 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 31 Jul 2020 11:42:13 -0300 +Subject: [PATCH] Modify tests to verify password was changed correctly. + +Modify and add tests to verify that a password change has the correct +effect on ipavault. +--- + tests/vault/test_vault_symmetric.yml | 36 ++++++++++++++++++---------- + 1 file changed, 23 insertions(+), 13 deletions(-) + +diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml +index bedc221..9294331 100644 +--- a/tests/vault/test_vault_symmetric.yml ++++ b/tests/vault/test_vault_symmetric.yml +@@ -178,6 +178,15 @@ + register: result + failed_when: result.vault.data != 'Hello World.' or result.changed + ++ - name: Retrieve data from symmetric vault, with wrong password. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ password: SomeWRONGpassword ++ state: retrieved ++ register: result ++ failed_when: not result.failed or "Invalid credentials" not in result.msg ++ + - name: Change vault password. + ipavault: + ipaadmin_password: SomeADMINpassword +@@ -187,43 +196,44 @@ + register: result + failed_when: not result.changed + +- - name: Retrieve data from symmetric vault, with wrong password. ++ - name: Retrieve data from symmetric vault, with new password. + ipavault: + ipaadmin_password: SomeADMINpassword + name: symvault +- password: SomeVAULTpassword ++ password: SomeNEWpassword + state: retrieved + register: result +- failed_when: not result.failed or "Invalid credentials" not in result.msg ++ failed_when: result.data != 'Hello World.' or result.changed + +- - name: Change vault password, with wrong `old_password`. ++ - name: Retrieve data from symmetric vault, with old password. + ipavault: + ipaadmin_password: SomeADMINpassword + name: symvault + password: SomeVAULTpassword +- new_password: SomeNEWpassword ++ state: retrieved + register: result + failed_when: not result.failed or "Invalid credentials" not in result.msg + +- - name: Retrieve data from symmetric vault, with new password. ++ - name: Change symmetric vault salt, changing password + ipavault: + ipaadmin_password: SomeADMINpassword + name: symvault + password: SomeNEWpassword +- state: retrieved ++ new_password: SomeVAULTpassword ++ salt: AAAAAAAAAAAAAAAAAAAAAAA= + register: result +- failed_when: result.vault.data != 'Hello World.' or result.changed ++ failed_when: not result.changed + +- - name: Try to add vault with multiple passwords. ++ - name: Change symmetric vault salt, without changing password + ipavault: + ipaadmin_password: SomeADMINpassword +- name: inexistentvault ++ name: symvault + password: SomeVAULTpassword +- password_file: "{{ ansible_env.HOME }}/password.txt" ++ new_password: SomeVAULTpassword ++ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo= + register: result +- failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg ++ failed_when: not result.changed + +- - name: Try to add vault with multiple new passwords. + ipavault: + ipaadmin_password: SomeADMINpassword + name: inexistentvault +-- +2.26.2 + +From 8ca282e276477b52d0850d4c01feb3d8e7a5be6d Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 31 Jul 2020 11:44:33 -0300 +Subject: [PATCH] Modified and added tests to verify correct `salt` update + behavior. + +--- + tests/vault/test_vault_symmetric.yml | 35 ++++++++++++++++++++++++---- + 1 file changed, 31 insertions(+), 4 deletions(-) + +diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml +index 9294331..1604a01 100644 +--- a/tests/vault/test_vault_symmetric.yml ++++ b/tests/vault/test_vault_symmetric.yml +@@ -234,14 +234,41 @@ + register: result + failed_when: not result.changed + ++ - name: Try to change symmetric vault salt, without providing any password + ipavault: + ipaadmin_password: SomeADMINpassword +- name: inexistentvault +- password: SomeVAULTpassword ++ name: symvault ++ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo= ++ register: result ++ failed_when: not result.failed and "Vault `salt` can only change when changing the password." not in result.msg ++ ++ - name: Try to change symmetric vault salt, without providing `password` ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo= + new_password: SomeVAULTpassword +- new_password_file: "{{ ansible_env.HOME }}/password.txt" + register: result +- failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg ++ failed_when: not result.failed and "Vault `salt` can only change when changing the password." not in result.msg ++ ++ - name: Try to change symmetric vault salt, without providing `new_password` ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo= ++ password: SomeVAULTpassword ++ register: result ++ failed_when: not result.failed and "Vault `salt` can only change when changing the password." not in result.msg ++ ++ - name: Try to change symmetric vault salt, using wrong password. ++ ipavault: ++ ipaadmin_password: SomeADMINpassword ++ name: symvault ++ password: SomeWRONGpassword ++ new_password: SomeWRONGpassword ++ salt: MDEyMzQ1Njc4OTAxMjM0NQo= ++ register: result ++ failed_when: not result.failed + + - name: Ensure symmetric vault is absent + ipavault: +-- +2.26.2 + +From 3c2700f68beade3513e0e44415d8eb4fb23026e8 Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Fri, 14 Aug 2020 10:43:30 -0300 +Subject: [PATCH] Fixed Vault return value usage from `data` to `vault.data`. + +A test was failing due to use of old ipavault module return structure +and some places on the documentation were alse referring to it. All +ocurrences were fixed. +--- + README-vault.md | 2 +- + plugins/modules/ipavault.py | 2 +- + tests/vault/test_vault_symmetric.yml | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/README-vault.md b/README-vault.md +index 91d311d..e7a31a2 100644 +--- a/README-vault.md ++++ b/README-vault.md +@@ -197,7 +197,7 @@ Example playbook to make sure vault is absent: + state: absent + register: result + - debug: +- msg: "{{ result.data }}" ++ msg: "{{ result.vault.data }}" + ``` + + Variables +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index a608e64..8060976 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -243,7 +243,7 @@ EXAMPLES = """ + state: retrieved + register: result + - debug: +- msg: "{{ result.data }}" ++ msg: "{{ result.vault.data }}" + + # Change password of a symmetric vault + - ipavault: +diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml +index 1604a01..5394c71 100644 +--- a/tests/vault/test_vault_symmetric.yml ++++ b/tests/vault/test_vault_symmetric.yml +@@ -203,7 +203,7 @@ + password: SomeNEWpassword + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Retrieve data from symmetric vault, with old password. + ipavault: +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-Fixes-service-disable-when-service-has-no-certificates-attached_rhbz#1836294.patch b/SOURCES/ansible-freeipa-0.1.12-Fixes-service-disable-when-service-has-no-certificates-attached_rhbz#1836294.patch new file mode 100644 index 0000000..b4b8e6e --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Fixes-service-disable-when-service-has-no-certificates-attached_rhbz#1836294.patch @@ -0,0 +1,112 @@ +From e57e4908f936c524085fb5853fe4493c7711ab3f Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Thu, 25 Jun 2020 16:26:30 -0300 +Subject: [PATCH] Fixes service disable when service has no certificates + attached. + +Services without certificates, but with keytabs were not being +disabled. This change allows execution of service_disable if +there is a certificate or if has_keytab is true. + +A new test was added to verify the issue: + + tests/service/test_service_disable.yml +--- + plugins/modules/ipaservice.py | 8 +-- + tests/service/test_service_disable.yml | 68 ++++++++++++++++++++++++++ + 2 files changed, 73 insertions(+), 3 deletions(-) + create mode 100644 tests/service/test_service_disable.yml + +diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py +index 23a0d6b3..b0d25355 100644 +--- a/plugins/modules/ipaservice.py ++++ b/plugins/modules/ipaservice.py +@@ -812,9 +812,11 @@ def main(): + + elif state == "disabled": + if action == "service": +- if res_find is not None and \ +- len(res_find.get('usercertificate', [])) > 0: +- commands.append([name, 'service_disable', {}]) ++ if res_find is not None: ++ has_cert = bool(res_find.get('usercertificate')) ++ has_keytab = res_find.get('has_keytab', False) ++ if has_cert or has_keytab: ++ commands.append([name, 'service_disable', {}]) + else: + ansible_module.fail_json( + msg="Invalid action '%s' for state '%s'" % +diff --git a/tests/service/test_service_disable.yml b/tests/service/test_service_disable.yml +new file mode 100644 +index 00000000..3b4a88fb +--- /dev/null ++++ b/tests/service/test_service_disable.yml +@@ -0,0 +1,68 @@ ++--- ++- name: Playbook to manage IPA service. ++ hosts: ipaserver ++ become: yes ++ gather_facts: yes ++ ++ tasks: ++ - name: Ensure service is absent ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "mysvc1/{{ ansible_fqdn }}" ++ ++ - name: Ensure service is present ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "mysvc1/{{ ansible_fqdn }}" ++ certificate: ++ - MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+QeNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqicuPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq ++ force: no ++ register: result ++ failed_when: not result.changed ++ ++ - name: Obtain keytab ++ shell: ipa-getkeytab -s "{{ ansible_fqdn }}" -p "mysvc1/{{ ansible_fqdn }}" -k mysvc1.keytab ++ ++ - name: Verify keytab ++ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}" ++ register: result ++ failed_when: result.failed or result.stdout | regex_search(" Keytab. true") ++ ++ - name: Ensure service is disabled ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "mysvc1/{{ ansible_fqdn }}" ++ state: disabled ++ register: result ++ failed_when: not result.changed ++ ++ - name: Verify keytab ++ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}" ++ register: result ++ failed_when: result.failed or result.stdout | regex_search(" Keytab. true") ++ ++ - name: Obtain keytab ++ shell: ipa-getkeytab -s "{{ ansible_fqdn }}" -p "mysvc1/{{ ansible_fqdn }}" -k mysvc1.keytab ++ ++ - name: Verify keytab ++ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}" ++ register: result ++ failed_when: result.failed or result.stdout | regex_search(" Keytab. true") ++ ++ - name: Ensure service is disabled ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "mysvc1/{{ ansible_fqdn }}" ++ state: disabled ++ register: result ++ failed_when: not result.changed ++ ++ - name: Verify keytab ++ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}" ++ register: result ++ failed_when: result.failed or result.stdout | regex_search(" Keytab. true") ++ ++ - name: Ensure service is absent ++ ipaservice: ++ ipaadmin_password: SomeADMINpassword ++ name: "mysvc1/{{ ansible_fqdn }}" diff --git a/SOURCES/ansible-freeipa-0.1.12-Modified-return-value-for-ipavault-module_rhbz#1867909.patch b/SOURCES/ansible-freeipa-0.1.12-Modified-return-value-for-ipavault-module_rhbz#1867909.patch new file mode 100644 index 0000000..0cba294 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-Modified-return-value-for-ipavault-module_rhbz#1867909.patch @@ -0,0 +1,300 @@ +From e96ef4e98e523f20c25777308c093ebbff272b2d Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Wed, 5 Aug 2020 15:24:15 -0300 +Subject: [PATCH] Updated documentation for ipavault module in the source code. + +This change fixes a wrong parameter name in the documentation of +RESULT_VALUES, and also provide a correct YAML snippet to ensure +presence of an asymmetric vault with a formatted private key. +--- + plugins/modules/ipavault.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index 46c6fcd..84645c7 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -267,7 +267,7 @@ EXAMPLES = """ + username: user01 + description: An asymmetric vault + vault_type: asymmetric +- public_key: ++ public_key: | + LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTR + HTkFEQ0JpUUtCZ1FDdGFudjRkK3ptSTZ0T3ova1RXdGowY3AxRAowUENoYy8vR0pJMTUzTi + 9CN3UrN0h3SXlRVlZoNUlXZG1UcCtkWXYzd09yeVpPbzYvbHN5eFJaZ2pZRDRwQ3VGCjlxM +@@ -303,7 +303,7 @@ EXAMPLES = """ + """ + + RETURN = """ +-user: ++data: + description: The vault data. + returned: If state is retrieved. + type: string +-- +2.26.2 + +From 7dd0b547c47b4fd617960490b8553a5036e3b30c Mon Sep 17 00:00:00 2001 +From: Rafael Guterres Jeffman +Date: Mon, 10 Aug 2020 16:02:09 -0300 +Subject: [PATCH] Modified return value for ipavault module. + +The ipavault module was returning a single string value when retrieving +data. To keep consistency with other modules, it should return a dict +with the `data` variable in it. + +This change modifies the result of ipavault to be a dict and also fixes +relevant tests, examples and documentation. +--- + README-vault.md | 5 +++++ + .../vault/retrive-data-asymmetric-vault.yml | 2 +- + .../vault/retrive-data-symmetric-vault.yml | 2 +- + plugins/modules/ipavault.py | 19 +++++++++++++------ + tests/vault/test_vault_asymmetric.yml | 12 ++++++------ + tests/vault/test_vault_standard.yml | 8 ++++---- + tests/vault/test_vault_symmetric.yml | 14 +++++++------- + 7 files changed, 37 insertions(+), 25 deletions(-) + +diff --git a/README-vault.md b/README-vault.md +index fa1d3e1..91d311d 100644 +--- a/README-vault.md ++++ b/README-vault.md +@@ -248,6 +248,11 @@ Variable | Description | Returned When + -------- | ----------- | ------------- + `data` | The data stored in the vault. | If `state` is `retrieved`. + ++Variable | Description | Returned When ++-------- | ----------- | ------------- ++`vault` | Vault dict with archived data. (dict)
Options: | If `state` is `retrieved`. ++  | `data` - The vault data. | Always ++ + + Notes + ===== +diff --git a/playbooks/vault/retrive-data-asymmetric-vault.yml b/playbooks/vault/retrive-data-asymmetric-vault.yml +index 5f67c59..f71f826 100644 +--- a/playbooks/vault/retrive-data-asymmetric-vault.yml ++++ b/playbooks/vault/retrive-data-asymmetric-vault.yml +@@ -14,4 +14,4 @@ + state: retrieved + register: result + - debug: +- msg: "Data: {{ result.data }}" ++ msg: "Data: {{ result.vault.data }}" +diff --git a/playbooks/vault/retrive-data-symmetric-vault.yml b/playbooks/vault/retrive-data-symmetric-vault.yml +index 163f8b9..24692a8 100644 +--- a/playbooks/vault/retrive-data-symmetric-vault.yml ++++ b/playbooks/vault/retrive-data-symmetric-vault.yml +@@ -14,4 +14,4 @@ + state: retrieved + register: result + - debug: +- msg: "{{ result.data | b64decode }}" ++ msg: "{{ result.vault.data }}" +diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py +index 84645c7..6a3c73e 100644 +--- a/plugins/modules/ipavault.py ++++ b/plugins/modules/ipavault.py +@@ -303,10 +303,15 @@ EXAMPLES = """ + """ + + RETURN = """ +-data: +- description: The vault data. +- returned: If state is retrieved. +- type: string ++vault: ++ description: Vault dict with archived data. ++ returned: If state is `retrieved`. ++ type: dict ++ options: ++ data: ++ description: The vault data. ++ returned: always ++ type: string + """ + + import os +@@ -910,9 +915,11 @@ def main(): + if 'result' not in result: + raise Exception("No result obtained.") + if 'data' in result['result']: +- exit_args['data'] = result['result']['data'] ++ data_return = exit_args.setdefault('vault', {}) ++ data_return['data'] = result['result']['data'] + elif 'vault_data' in result['result']: +- exit_args['data'] = result['result']['vault_data'] ++ data_return = exit_args.setdefault('vault', {}) ++ data_return['data'] = result['result']['vault_data'] + else: + raise Exception("No data retrieved.") + changed = False +diff --git a/tests/vault/test_vault_asymmetric.yml b/tests/vault/test_vault_asymmetric.yml +index 1a1d3dc..268922c 100644 +--- a/tests/vault/test_vault_asymmetric.yml ++++ b/tests/vault/test_vault_asymmetric.yml +@@ -42,7 +42,7 @@ + private_key: "{{ lookup('file', 'private.pem') | b64encode }}" + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Retrieve data from asymmetric vault into file {{ ansible_env.HOME }}/data.txt. + ipavault: +@@ -75,7 +75,7 @@ + private_key: "{{ lookup('file', 'private.pem') | b64encode }}" + state: retrieved + register: result +- failed_when: result.data != 'The world of Ï€ is half rounded.' or result.changed ++ failed_when: result.vault.data != 'The world of Ï€ is half rounded.' or result.changed + + - name: Archive data in asymmetric vault, from file. + ipavault: +@@ -93,7 +93,7 @@ + private_key: "{{ lookup('file', 'private.pem') | b64encode }}" + state: retrieved + register: result +- failed_when: result.data != 'Another World.' or result.changed ++ failed_when: result.vault.data != 'Another World.' or result.changed + + - name: Archive data with single character to asymmetric vault + ipavault: +@@ -110,7 +110,7 @@ + private_key: "{{ lookup('file', 'private.pem') | b64encode }}" + state: retrieved + register: result +- failed_when: result.data != 'c' or result.changed ++ failed_when: result.vault.data != 'c' or result.changed + + - name: Ensure asymmetric vault is absent + ipavault: +@@ -161,7 +161,7 @@ + private_key: "{{ lookup('file', 'private.pem') | b64encode }}" + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Retrieve data from asymmetric vault, with password file. + ipavault: +@@ -170,7 +170,7 @@ + private_key_file: "{{ ansible_env.HOME }}/private.pem" + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Ensure asymmetric vault is absent + ipavault: +diff --git a/tests/vault/test_vault_standard.yml b/tests/vault/test_vault_standard.yml +index 5e0da98..6ccb0d5 100644 +--- a/tests/vault/test_vault_standard.yml ++++ b/tests/vault/test_vault_standard.yml +@@ -39,7 +39,7 @@ + name: stdvault + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Retrieve data from standard vault into file {{ ansible_env.HOME }}/data.txt. + ipavault: +@@ -70,7 +70,7 @@ + name: stdvault + state: retrieved + register: result +- failed_when: result.data != 'The world of Ï€ is half rounded.' or result.changed ++ failed_when: result.vault.data != 'The world of Ï€ is half rounded.' or result.changed + + - name: Archive data in standard vault, from file. + ipavault: +@@ -87,7 +87,7 @@ + name: stdvault + state: retrieved + register: result +- failed_when: result.data != 'Another World.' or result.changed ++ failed_when: result.vault.data != 'Another World.' or result.changed + + - name: Archive data with single character to standard vault + ipavault: +@@ -103,7 +103,7 @@ + name: stdvault + state: retrieved + register: result +- failed_when: result.data != 'c' or result.changed ++ failed_when: result.vault.data != 'c' or result.changed + + - name: Ensure standard vault is absent + ipavault: +diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml +index a6072d8..bedc221 100644 +--- a/tests/vault/test_vault_symmetric.yml ++++ b/tests/vault/test_vault_symmetric.yml +@@ -43,7 +43,7 @@ + password: SomeVAULTpassword + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Retrieve data from symmetric vault into file {{ ansible_env.HOME }}/data.txt. + ipavault: +@@ -77,7 +77,7 @@ + password: SomeVAULTpassword + state: retrieved + register: result +- failed_when: result.data != 'The world of Ï€ is half rounded.' or result.changed ++ failed_when: result.vault.data != 'The world of Ï€ is half rounded.' or result.changed + + - name: Archive data in symmetric vault, from file. + ipavault: +@@ -95,7 +95,7 @@ + password: SomeVAULTpassword + state: retrieved + register: result +- failed_when: result.data != 'Another World.' or result.changed ++ failed_when: result.vault.data != 'Another World.' or result.changed + + - name: Archive data with single character to symmetric vault + ipavault: +@@ -113,7 +113,7 @@ + password: SomeVAULTpassword + state: retrieved + register: result +- failed_when: result.data != 'c' or result.changed ++ failed_when: result.vault.data != 'c' or result.changed + + - name: Ensure symmetric vault is absent + ipavault: +@@ -167,7 +167,7 @@ + password: SomeVAULTpassword + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Retrieve data from symmetric vault, with password file. + ipavault: +@@ -176,7 +176,7 @@ + password_file: "{{ ansible_env.HOME }}/password.txt" + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Change vault password. + ipavault: +@@ -212,7 +212,7 @@ + password: SomeNEWpassword + state: retrieved + register: result +- failed_when: result.data != 'Hello World.' or result.changed ++ failed_when: result.vault.data != 'Hello World.' or result.changed + + - name: Try to add vault with multiple passwords. + ipavault: +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-action_plugins-ipaclient_get_otp-Discovered-python-n_rhbz#1852714.patch b/SOURCES/ansible-freeipa-0.1.12-action_plugins-ipaclient_get_otp-Discovered-python-n_rhbz#1852714.patch new file mode 100644 index 0000000..14dbf4e --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-action_plugins-ipaclient_get_otp-Discovered-python-n_rhbz#1852714.patch @@ -0,0 +1,49 @@ +From 80aac15de9026055ae2b9972859939cf7925b813 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Tue, 30 Jun 2020 17:32:19 +0200 +Subject: [PATCH] action_plugins/ipaclient_get_otp: Discovered python needed in + task_vars + +Ansible is now also supporting discovered_python_interpreter for +action_plugins. task_vars needs to be non Null and contain a setting for +discovered_python_interpreter. The ipaclient_get_otp action_plugin +therefore needed to be adapted. +--- + roles/ipaclient/action_plugins/ipaclient_get_otp.py | 4 ++-- + roles/ipaclient/tasks/install.yml | 1 - + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/roles/ipaclient/action_plugins/ipaclient_get_otp.py b/roles/ipaclient/action_plugins/ipaclient_get_otp.py +index dcddc0a..8e04ad9 100644 +--- a/roles/ipaclient/action_plugins/ipaclient_get_otp.py ++++ b/roles/ipaclient/action_plugins/ipaclient_get_otp.py +@@ -164,7 +164,8 @@ class ActionModule(ActionBase): + return result + + data = self._execute_module(module_name='ipaclient_get_facts', +- module_args=dict(), task_vars=None) ++ module_args=dict(), task_vars=task_vars) ++ + try: + domain = data['ansible_facts']['ipa']['domain'] + realm = data['ansible_facts']['ipa']['realm'] +@@ -245,4 +246,3 @@ class ActionModule(ActionBase): + finally: + # delete the local temp directory + shutil.rmtree(local_temp_dir, ignore_errors=True) +- run_cmd(['/usr/bin/kdestroy', '-c', tmp_ccache]) +diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml +index 0de3dea..4421f0c 100644 +--- a/roles/ipaclient/tasks/install.yml ++++ b/roles/ipaclient/tasks/install.yml +@@ -134,7 +134,6 @@ + "Password cannot be set on enrolled host" not + in result_ipaclient_get_otp.msg + delegate_to: "{{ result_ipaclient_test.servers[0] }}" +- delegate_facts: yes + ignore_errors: yes + + - name: Install - Report error for OTP generation +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-ipa-host-group-Fix-membermanager-unknow-user-issue_rhbz#1848426.patch b/SOURCES/ansible-freeipa-0.1.12-ipa-host-group-Fix-membermanager-unknow-user-issue_rhbz#1848426.patch new file mode 100644 index 0000000..69069b2 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-ipa-host-group-Fix-membermanager-unknow-user-issue_rhbz#1848426.patch @@ -0,0 +1,132 @@ +From 6132a947e65fb9c3a1ec5c059aed34afb06a67df Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Mon, 29 Jun 2020 13:12:12 +0200 +Subject: [PATCH] ipa[host]group: Fix membermanager unknow user issue + +If a unknown membermanager user presence will be ensured, the unknown user +error was ignored. This has been fixed in ipagroup. The code for the error +handling in ipagroup and ipahostgroup has been adapted because of this. + +New tests for tests/[host]group/test_[host]group_membermnager.yml have been +added. +--- + plugins/modules/ipagroup.py | 19 +++++++++---------- + plugins/modules/ipahostgroup.py | 13 +++++++------ + tests/group/test_group_membermanager.yml | 11 ++++++++++- + .../test_hostgroup_membermanager.yml | 11 ++++++++++- + 4 files changed, 36 insertions(+), 18 deletions(-) + +diff --git a/plugins/modules/ipagroup.py b/plugins/modules/ipagroup.py +index 915bc499..903c256d 100644 +--- a/plugins/modules/ipagroup.py ++++ b/plugins/modules/ipagroup.py +@@ -507,16 +507,15 @@ def main(): + # All "already a member" and "not a member" failures in the + # result are ignored. All others are reported. + errors = [] +- if "failed" in result and len(result["failed"]) > 0: +- for item in result["failed"]: +- failed_item = result["failed"][item] +- for member_type in failed_item: +- for member, failure in failed_item[member_type]: +- if "already a member" in failure \ +- or "not a member" in failure: +- continue +- errors.append("%s: %s %s: %s" % ( +- command, member_type, member, failure)) ++ for failed_item in result.get("failed", []): ++ failed = result["failed"][failed_item] ++ for member_type in failed: ++ for member, failure in failed[member_type]: ++ if "already a member" in failure \ ++ or "not a member" in failure: ++ continue ++ errors.append("%s: %s %s: %s" % ( ++ command, member_type, member, failure)) + if len(errors) > 0: + ansible_module.fail_json(msg=", ".join(errors)) + +diff --git a/plugins/modules/ipahostgroup.py b/plugins/modules/ipahostgroup.py +index 4c18e940..5f615160 100644 +--- a/plugins/modules/ipahostgroup.py ++++ b/plugins/modules/ipahostgroup.py +@@ -423,14 +423,15 @@ def main(): + # All "already a member" and "not a member" failures in the + # result are ignored. All others are reported. + errors = [] +- if "failed" in result and "member" in result["failed"]: +- failed = result["failed"]["member"] ++ for failed_item in result.get("failed", []): ++ failed = result["failed"][failed_item] + for member_type in failed: + for member, failure in failed[member_type]: +- if "already a member" not in failure \ +- and "not a member" not in failure: +- errors.append("%s: %s %s: %s" % ( +- command, member_type, member, failure)) ++ if "already a member" in failure \ ++ or "not a member" in failure: ++ continue ++ errors.append("%s: %s %s: %s" % ( ++ command, member_type, member, failure)) + if len(errors) > 0: + ansible_module.fail_json(msg=", ".join(errors)) + +diff --git a/tests/group/test_group_membermanager.yml b/tests/group/test_group_membermanager.yml +index 1d38654f..661f26d6 100644 +--- a/tests/group/test_group_membermanager.yml ++++ b/tests/group/test_group_membermanager.yml +@@ -8,7 +8,7 @@ + - name: Ensure user manangeruser1 and manageruser2 is absent + ipauser: + ipaadmin_password: SomeADMINpassword +- name: manageruser1,manageruser2 ++ name: manageruser1,manageruser2,unknown_user + state: absent + + - name: Ensure group testgroup, managergroup1 and managergroup2 are absent +@@ -185,6 +185,15 @@ + register: result + failed_when: not result.changed + ++ - name: Ensure unknown membermanager_user member failure ++ ipagroup: ++ ipaadmin_password: SomeADMINpassword ++ name: testgroup ++ membermanager_user: unknown_user ++ action: member ++ register: result ++ failed_when: result.changed or "no such entry" not in result.msg ++ + - name: Ensure group testgroup, managergroup1 and managergroup2 are absent + ipagroup: + ipaadmin_password: SomeADMINpassword +diff --git a/tests/hostgroup/test_hostgroup_membermanager.yml b/tests/hostgroup/test_hostgroup_membermanager.yml +index c32d1088..c0f65460 100644 +--- a/tests/hostgroup/test_hostgroup_membermanager.yml ++++ b/tests/hostgroup/test_hostgroup_membermanager.yml +@@ -15,7 +15,7 @@ + - name: Ensure user manangeruser1 and manageruser2 is absent + ipauser: + ipaadmin_password: SomeADMINpassword +- name: manageruser1,manageruser2 ++ name: manageruser1,manageruser2,unknown_user + state: absent + + - name: Ensure group managergroup1 and managergroup2 are absent +@@ -200,6 +200,15 @@ + register: result + failed_when: not result.changed + ++ - name: Ensure unknown membermanager_user member failure ++ ipahostgroup: ++ ipaadmin_password: SomeADMINpassword ++ name: testhostgroup ++ membermanager_user: unknown_user ++ action: member ++ register: result ++ failed_when: result.changed or "no such entry" not in result.msg ++ + - name: Ensure host-group testhostgroup is absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword diff --git a/SOURCES/ansible-freeipa-0.1.12-ipa-server-replica-Fix-pkcs12-info-regressions-intro_rhbz#1853284.patch b/SOURCES/ansible-freeipa-0.1.12-ipa-server-replica-Fix-pkcs12-info-regressions-intro_rhbz#1853284.patch new file mode 100644 index 0000000..ab45af0 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-ipa-server-replica-Fix-pkcs12-info-regressions-intro_rhbz#1853284.patch @@ -0,0 +1,150 @@ +From 8ce5fd147aafc34e43dbe4246565c48eace2e115 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Thu, 2 Jul 2020 12:02:33 +0200 +Subject: [PATCH] ipa[server,replica]: Fix pkcs12 info regressions introduced + with CA-less + +With the CA-less patches the types for the pkcs12 infos have been changed +to lists in the modules. This is resulting in a bad conversion from None +to [''] for the parameters. Because of this a normal replica deployment is +failing as [''] is not a valid value. + +The install.yml files for ipareplica and also ipaserver have been changed +in the way that the pkcs12 values are checked if they are None. The +parameter will simply be omitted in this case and the parameter in the +module will become None by default. +--- + roles/ipareplica/tasks/install.yml | 18 +++++++++--------- + roles/ipaserver/tasks/install.yml | 10 +++++----- + 2 files changed, 14 insertions(+), 14 deletions(-) + +diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml +index fc7f83e..c2a6222 100644 +--- a/roles/ipareplica/tasks/install.yml ++++ b/roles/ipareplica/tasks/install.yml +@@ -281,7 +281,7 @@ + ccache: "{{ result_ipareplica_prepare.ccache }}" + installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}" + _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" +- _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}" ++ _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}" + subject_base: "{{ result_ipareplica_prepare.subject_base }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}" +@@ -345,7 +345,7 @@ + config_master_host_name: + "{{ result_ipareplica_install_ca_certs.config_master_host_name }}" + ccache: "{{ result_ipareplica_prepare.ccache }}" +- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" ++ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + + # We need to point to the master in ipa default conf when certmonger +@@ -407,8 +407,8 @@ + ccache: "{{ result_ipareplica_prepare.ccache }}" + _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" + _ca_file: "{{ result_ipareplica_prepare._ca_file }}" +- _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}" +- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" ++ _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}" ++ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + dirman_password: "{{ ipareplica_dirman_password }}" + ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}" +@@ -429,7 +429,7 @@ + ccache: "{{ result_ipareplica_prepare.ccache }}" + _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" + _ca_file: "{{ result_ipareplica_prepare._ca_file }}" +- _http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info }}" ++ _http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info if result_ipareplica_prepare._http_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + dirman_password: "{{ ipareplica_dirman_password }}" + +@@ -507,7 +507,7 @@ + _kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}" + _kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}" + _ca_file: "{{ result_ipareplica_prepare._ca_file }}" +- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" ++ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + dirman_password: "{{ ipareplica_dirman_password }}" + +@@ -529,7 +529,7 @@ + _kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}" + _kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}" + _subject_base: "{{ result_ipareplica_prepare._subject_base }}" +- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" ++ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + dirman_password: "{{ ipareplica_dirman_password }}" + config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}" +@@ -554,7 +554,7 @@ + ccache: "{{ result_ipareplica_prepare.ccache }}" + _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" + _ca_file: "{{ result_ipareplica_prepare._ca_file }}" +- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" ++ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + dirman_password: "{{ ipareplica_dirman_password }}" + +@@ -574,7 +574,7 @@ + ccache: "{{ result_ipareplica_prepare.ccache }}" + _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" + _ca_file: "{{ result_ipareplica_prepare._ca_file }}" +- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" ++ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + dirman_password: "{{ ipareplica_dirman_password }}" + ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}" +diff --git a/roles/ipaserver/tasks/install.yml b/roles/ipaserver/tasks/install.yml +index 30f9da2..687f72d 100644 +--- a/roles/ipaserver/tasks/install.yml ++++ b/roles/ipaserver/tasks/install.yml +@@ -203,7 +203,7 @@ + # no_host_dns: "{{ result_ipaserver_test.no_host_dns }}" + dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}" + dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default(omit) }}" +- _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}" ++ _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}" + external_cert_files: + "{{ ipaserver_external_cert_files | default(omit) }}" + subject_base: "{{ result_ipaserver_prepare.subject_base }}" +@@ -240,7 +240,7 @@ + no_hbac_allow: "{{ ipaserver_no_hbac_allow }}" + idstart: "{{ result_ipaserver_test.idstart }}" + idmax: "{{ result_ipaserver_test.idmax }}" +- _pkinit_pkcs12_info: "{{ result_ipaserver_test._pkinit_pkcs12_info }}" ++ _pkinit_pkcs12_info: "{{ result_ipaserver_test._pkinit_pkcs12_info if result_ipaserver_test._pkinit_pkcs12_info != None else omit }}" + + - name: Install - Setup custodia + ipaserver_setup_custodia: +@@ -270,7 +270,7 @@ + no_pkinit: "{{ result_ipaserver_test.no_pkinit }}" + dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}" + dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}" +- _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}" ++ _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}" + external_ca: "{{ ipaserver_external_ca }}" + external_ca_type: "{{ ipaserver_external_ca_type | default(omit) }}" + external_ca_profile: +@@ -334,7 +334,7 @@ + idmax: "{{ result_ipaserver_test.idmax }}" + http_cert_files: "{{ ipaserver_http_cert_files | default([]) }}" + no_ui_redirect: "{{ ipaserver_no_ui_redirect }}" +- _http_pkcs12_info: "{{ result_ipaserver_test._http_pkcs12_info }}" ++ _http_pkcs12_info: "{{ result_ipaserver_test._http_pkcs12_info if result_ipaserver_test._http_pkcs12_info != None else omit }}" + + - name: Install - Setup KRA + ipaserver_setup_kra: +@@ -394,7 +394,7 @@ + idstart: "{{ result_ipaserver_test.idstart }}" + idmax: "{{ result_ipaserver_test.idmax }}" + dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}" +- _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}" ++ _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}" + + - name: Install - Setup client + include_role: +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.12-ipa-user,host-Fail-on-duplucate-names-in-the-users-and-hosts-lists_rhbz#1822683.patch b/SOURCES/ansible-freeipa-0.1.12-ipa-user,host-Fail-on-duplucate-names-in-the-users-and-hosts-lists_rhbz#1822683.patch new file mode 100644 index 0000000..1bf52b7 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-ipa-user,host-Fail-on-duplucate-names-in-the-users-and-hosts-lists_rhbz#1822683.patch @@ -0,0 +1,132 @@ +From 1d7fb31b8bfa00babd7c753b354d7344b531cd77 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Mon, 29 Jun 2020 14:50:56 +0200 +Subject: [PATCH] ipa[user,host]: Fail on duplucate names in the users and + hosts lists + +It was possible to have several entries for names with the hosts and users +lists. This resulted sometimes in errors but also unexpected changes. A new +check has been added to make sure that the names in the users and hosts +lists are unique. + +New tests have been added to verify this in the existing files: +- tests/host/test_hosts.yml +- tests/user/test_users.yml +--- + plugins/modules/ipahost.py | 7 +++++++ + plugins/modules/ipauser.py | 7 +++++++ + tests/host/test_hosts.yml | 15 +++++++++++++++ + tests/user/test_users.yml | 19 +++++++++++++++++++ + 4 files changed, 48 insertions(+) + +diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py +index 7a981f16..1fe11dc5 100644 +--- a/plugins/modules/ipahost.py ++++ b/plugins/modules/ipahost.py +@@ -799,10 +799,15 @@ def main(): + server_realm = api_get_realm() + + commands = [] ++ host_set = set() + + for host in names: + if isinstance(host, dict): + name = host.get("name") ++ if name in host_set: ++ ansible_module.fail_json( ++ msg="host '%s' is used more than once" % name) ++ host_set.add(name) + description = host.get("description") + locality = host.get("locality") + location = host.get("location") +@@ -1337,6 +1342,8 @@ def main(): + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + ++ del host_set ++ + # Execute commands + + errors = [] +diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py +index b8152ee4..03713a41 100644 +--- a/plugins/modules/ipauser.py ++++ b/plugins/modules/ipauser.py +@@ -958,10 +958,15 @@ def main(): + # commands + + commands = [] ++ user_set = set() + + for user in names: + if isinstance(user, dict): + name = user.get("name") ++ if name in user_set: ++ ansible_module.fail_json( ++ msg="user '%s' is used more than once" % name) ++ user_set.add(name) + # present + first = user.get("first") + last = user.get("last") +@@ -1370,6 +1375,8 @@ def main(): + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + ++ del user_set ++ + # Execute commands + + errors = [] +diff --git a/tests/host/test_hosts.yml b/tests/host/test_hosts.yml +index 30fd6538..f82cc612 100644 +--- a/tests/host/test_hosts.yml ++++ b/tests/host/test_hosts.yml +@@ -96,3 +96,18 @@ + state: absent + register: result + failed_when: result.changed ++ ++ - name: Duplicate names in hosts failure test ++ ipahost: ++ ipaadmin_password: SomeADMINpassword ++ hosts: ++ - name: "{{ host1_fqdn }}" ++ force: yes ++ - name: "{{ host2_fqdn }}" ++ force: yes ++ - name: "{{ host3_fqdn }}" ++ force: yes ++ - name: "{{ host3_fqdn }}" ++ force: yes ++ register: result ++ failed_when: result.changed or "is used more than once" not in result.msg +diff --git a/tests/user/test_users.yml b/tests/user/test_users.yml +index 5b5d4538..81c7b608 100644 +--- a/tests/user/test_users.yml ++++ b/tests/user/test_users.yml +@@ -85,6 +85,25 @@ + register: result + failed_when: result.changed + ++ - name: Duplicate names in users failure test ++ ipauser: ++ ipaadmin_password: SomeADMINpassword ++ users: ++ - name: user1 ++ givenname: user1 ++ last: Last ++ - name: user2 ++ first: user2 ++ last: Last ++ - name: user3 ++ first: user3 ++ last: Last ++ - name: user3 ++ first: user3 ++ last: Last ++ register: result ++ failed_when: result.changed or "is used more than once" not in result.msg ++ + - name: Remove test users + ipauser: + ipaadmin_password: SomeADMINpassword diff --git a/SOURCES/ansible-freeipa-0.1.12-ipareplica-Fix-missing-parameters-for-several-module_hbz#1855299.patch b/SOURCES/ansible-freeipa-0.1.12-ipareplica-Fix-missing-parameters-for-several-module_hbz#1855299.patch new file mode 100644 index 0000000..ad3ce22 --- /dev/null +++ b/SOURCES/ansible-freeipa-0.1.12-ipareplica-Fix-missing-parameters-for-several-module_hbz#1855299.patch @@ -0,0 +1,271 @@ +From 7a2eaa6f535b1353d46bcfa8b0b2484b15ff3863 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Tue, 7 Jul 2020 17:13:09 +0200 +Subject: [PATCH] ipareplica: Fix missing parameters for several modules + +The parameters master_host_name, config_setup_ca, dirman_password have not +been set for some modules. Also there was no ldap2 connection within +ipareplica_setup_kra. All this resulted in improper configuration where +for example KRA deployment failed in the end. + +A conversion warning in ipareplica_setup_adtrust has also been fixed for +the setup_ca parameter. + +Fixes #314 (IPA replica installation failure - DS enabled SSL - second part) +--- + .../library/ipareplica_create_ipa_conf.py | 1 + + .../library/ipareplica_ds_apply_updates.py | 1 + + .../library/ipareplica_ds_enable_ssl.py | 1 + + .../library/ipareplica_setup_adtrust.py | 2 +- + .../library/ipareplica_setup_custodia.py | 1 + + .../library/ipareplica_setup_http.py | 2 +- + .../ipareplica/library/ipareplica_setup_kra.py | 18 ++++++++++++++++++ + .../ipareplica/library/ipareplica_setup_krb.py | 7 +++++++ + roles/ipareplica/tasks/install.yml | 8 ++++++++ + 9 files changed, 39 insertions(+), 2 deletions(-) + +diff --git a/roles/ipareplica/library/ipareplica_create_ipa_conf.py b/roles/ipareplica/library/ipareplica_create_ipa_conf.py +index 3a85a6f..c475469 100644 +--- a/roles/ipareplica/library/ipareplica_create_ipa_conf.py ++++ b/roles/ipareplica/library/ipareplica_create_ipa_conf.py +@@ -262,6 +262,7 @@ def main(): + config.subject_base = options.subject_base + config.dirman_password = dirman_password + config.ca_host_name = ca_host_name ++ config.setup_ca = options.setup_ca + + remote_api = gen_remote_api(master_host_name, paths.ETC_IPA) + installer._remote_api = remote_api +diff --git a/roles/ipareplica/library/ipareplica_ds_apply_updates.py b/roles/ipareplica/library/ipareplica_ds_apply_updates.py +index 3796874..71008b3 100644 +--- a/roles/ipareplica/library/ipareplica_ds_apply_updates.py ++++ b/roles/ipareplica/library/ipareplica_ds_apply_updates.py +@@ -177,6 +177,7 @@ def main(): + config = gen_ReplicaConfig() + config.dirman_password = dirman_password + config.subject_base = options.subject_base ++ config.master_host_name = master_host_name + + remote_api = gen_remote_api(master_host_name, paths.ETC_IPA) + +diff --git a/roles/ipareplica/library/ipareplica_ds_enable_ssl.py b/roles/ipareplica/library/ipareplica_ds_enable_ssl.py +index a1b638e..3e4090d 100644 +--- a/roles/ipareplica/library/ipareplica_ds_enable_ssl.py ++++ b/roles/ipareplica/library/ipareplica_ds_enable_ssl.py +@@ -173,6 +173,7 @@ def main(): + config = gen_ReplicaConfig() + config.dirman_password = dirman_password + config.subject_base = options.subject_base ++ config.master_host_name = master_host_name + + remote_api = gen_remote_api(master_host_name, paths.ETC_IPA) + # installer._remote_api = remote_api +diff --git a/roles/ipareplica/library/ipareplica_setup_adtrust.py b/roles/ipareplica/library/ipareplica_setup_adtrust.py +index c830ebf..734e56d 100644 +--- a/roles/ipareplica/library/ipareplica_setup_adtrust.py ++++ b/roles/ipareplica/library/ipareplica_setup_adtrust.py +@@ -110,7 +110,7 @@ def main(): + # additional + ccache=dict(required=True), + _top_dir=dict(required=True), +- setup_ca=dict(required=True), ++ setup_ca=dict(required=True, type='bool'), + config_master_host_name=dict(required=True), + ), + supports_check_mode=True, +diff --git a/roles/ipareplica/library/ipareplica_setup_custodia.py b/roles/ipareplica/library/ipareplica_setup_custodia.py +index 5a74e87..2e95c26 100644 +--- a/roles/ipareplica/library/ipareplica_setup_custodia.py ++++ b/roles/ipareplica/library/ipareplica_setup_custodia.py +@@ -169,6 +169,7 @@ def main(): + config.promote = installer.promote + config.kra_enabled = kra_enabled + config.kra_host_name = kra_host_name ++ config.setup_ca = options.setup_ca + + remote_api = gen_remote_api(master_host_name, paths.ETC_IPA) + +diff --git a/roles/ipareplica/library/ipareplica_setup_http.py b/roles/ipareplica/library/ipareplica_setup_http.py +index 987ea95..3fa4807 100644 +--- a/roles/ipareplica/library/ipareplica_setup_http.py ++++ b/roles/ipareplica/library/ipareplica_setup_http.py +@@ -164,7 +164,7 @@ def main(): + config.subject_base = options.subject_base + config.dirman_password = dirman_password + config.setup_ca = options.setup_ca +- # config.master_host_name = master_host_name ++ config.master_host_name = master_host_name + config.ca_host_name = ca_host_name + config.promote = installer.promote + +diff --git a/roles/ipareplica/library/ipareplica_setup_kra.py b/roles/ipareplica/library/ipareplica_setup_kra.py +index 3149c10..0b2f681 100644 +--- a/roles/ipareplica/library/ipareplica_setup_kra.py ++++ b/roles/ipareplica/library/ipareplica_setup_kra.py +@@ -120,6 +120,9 @@ options: + _subject_base: + description: The installer _subject_base setting + required: no ++ dirman_password: ++ description: Directory Manager (master) password ++ required: no + author: + - Thomas Woerner + ''' +@@ -173,10 +176,12 @@ def main(): + _ca_enabled=dict(required=False, type='bool'), + _kra_enabled=dict(required=False, type='bool'), + _kra_host_name=dict(required=False), ++ _ca_host_name=dict(required=False), + _top_dir=dict(required=True), + _add_to_ipaservers=dict(required=True, type='bool'), + _ca_subject=dict(required=True), + _subject_base=dict(required=True), ++ dirman_password=dict(required=True, no_log=True), + ), + supports_check_mode=True, + ) +@@ -233,6 +238,7 @@ def main(): + ca_enabled = ansible_module.params.get('_ca_enabled') + kra_enabled = ansible_module.params.get('_kra_enabled') + kra_host_name = ansible_module.params.get('_kra_host_name') ++ ca_host_name = ansible_module.params.get('_ca_host_name') + + options.subject_base = ansible_module.params.get('subject_base') + if options.subject_base is not None: +@@ -243,6 +249,7 @@ def main(): + + options._ca_subject = ansible_module.params.get('_ca_subject') + options._subject_base = ansible_module.params.get('_subject_base') ++ dirman_password = ansible_module.params.get('dirman_password') + + # init # + +@@ -254,14 +261,25 @@ def main(): + constants.DEFAULT_CONFIG) + api_bootstrap_finalize(env) + config = gen_ReplicaConfig() ++ config.dirman_password = dirman_password + config.subject_base = options.subject_base + config.promote = installer.promote + config.kra_enabled = kra_enabled + config.kra_host_name = kra_host_name ++ config.ca_host_name = ca_host_name ++ config.master_host_name = master_host_name + + remote_api = gen_remote_api(master_host_name, paths.ETC_IPA) + installer._remote_api = remote_api + ++ conn = remote_api.Backend.ldap2 ++ ccache = os.environ['KRB5CCNAME'] ++ ++ # There is a api.Backend.ldap2.connect call somewhere in ca, ds, dns or ++ # ntpinstance ++ api.Backend.ldap2.connect() ++ conn.connect(ccache=ccache) ++ + with redirect_stdout(ansible_log): + ansible_log.debug("-- INSTALL KRA --") + +diff --git a/roles/ipareplica/library/ipareplica_setup_krb.py b/roles/ipareplica/library/ipareplica_setup_krb.py +index c8d09f7..4500a6f 100644 +--- a/roles/ipareplica/library/ipareplica_setup_krb.py ++++ b/roles/ipareplica/library/ipareplica_setup_krb.py +@@ -63,6 +63,9 @@ options: + _top_dir: + description: The installer _top_dir setting + required: no ++ dirman_password: ++ description: Directory Manager (master) password ++ required: no + author: + - Thomas Woerner + ''' +@@ -98,6 +101,7 @@ def main(): + ccache=dict(required=True), + _pkinit_pkcs12_info=dict(required=False, type='list'), + _top_dir=dict(required=True), ++ dirman_password=dict(required=True, no_log=True), + ), + supports_check_mode=True, + ) +@@ -126,6 +130,7 @@ def main(): + '_pkinit_pkcs12_info') + + options._top_dir = ansible_module.params.get('_top_dir') ++ dirman_password = ansible_module.params.get('dirman_password') + + # init # + +@@ -141,8 +146,10 @@ def main(): + constants.DEFAULT_CONFIG) + api_bootstrap_finalize(env) + config = gen_ReplicaConfig() ++ config.dirman_password = dirman_password + config.master_host_name = config_master_host_name + config.subject_base = options.subject_base ++ config.setup_ca = options.setup_ca + + ccache = os.environ['KRB5CCNAME'] + +diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml +index c2a6222..ddb3f85 100644 +--- a/roles/ipareplica/tasks/install.yml ++++ b/roles/ipareplica/tasks/install.yml +@@ -226,6 +226,8 @@ + setup_adtrust: "{{ result_ipareplica_test.setup_adtrust }}" + setup_kra: "{{ result_ipareplica_test.setup_kra }}" + setup_dns: "{{ ipareplica_setup_dns }}" ++ ### server ### ++ setup_ca: "{{ ipareplica_setup_ca }}" + ### ssl certificate ### + dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}" + ### client ### +@@ -332,6 +334,7 @@ + _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}" + _subject_base: "{{ result_ipareplica_prepare._subject_base }}" + dirman_password: "{{ ipareplica_dirman_password }}" ++ setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}" + + - name: Install - Setup KRB + ipareplica_setup_krb: +@@ -347,6 +350,7 @@ + ccache: "{{ result_ipareplica_prepare.ccache }}" + _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" ++ dirman_password: "{{ ipareplica_dirman_password }}" + + # We need to point to the master in ipa default conf when certmonger + # asks for HTTP certificate in newer ipa versions. In these versions +@@ -388,6 +392,7 @@ + _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}" + _subject_base: "{{ result_ipareplica_prepare._subject_base }}" + dirman_password: "{{ ipareplica_dirman_password }}" ++ setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}" + master: + "{{ result_ipareplica_install_ca_certs.config_master_host_name }}" + when: result_ipareplica_test.change_master_for_certmonger +@@ -471,6 +476,7 @@ + _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}" + _subject_base: "{{ result_ipareplica_prepare._subject_base }}" + dirman_password: "{{ ipareplica_dirman_password }}" ++ setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}" + when: result_ipareplica_test.change_master_for_certmonger + + - name: Install - Setup otpd +@@ -611,10 +617,12 @@ + _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" + _kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}" + _kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}" ++ _ca_host_name: "{{ result_ipareplica_prepare.config_ca_host_name }}" + _top_dir: "{{ result_ipareplica_prepare._top_dir }}" + _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}" + _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}" + _subject_base: "{{ result_ipareplica_prepare._subject_base }}" ++ dirman_password: "{{ ipareplica_dirman_password }}" + when: result_ipareplica_test.setup_kra + + - name: Install - Restart KDC +-- +2.26.2 + diff --git a/SOURCES/ansible-freeipa-0.1.8-Add-missing-attributes-to-ipasudorule_rhbz#1788168,1788035,1788024.patch b/SOURCES/ansible-freeipa-0.1.8-Add-missing-attributes-to-ipasudorule_rhbz#1788168,1788035,1788024.patch deleted file mode 100644 index f5c5147..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-Add-missing-attributes-to-ipasudorule_rhbz#1788168,1788035,1788024.patch +++ /dev/null @@ -1,1129 +0,0 @@ -From dc0a5585fb036fbeba2200564e26c478465afeec Mon Sep 17 00:00:00 2001 -From: Rafael Guterres Jeffman -Date: Tue, 31 Dec 2019 11:04:49 -0300 -Subject: [PATCH] Add missing attributes to ipasudorule. - -This patch adds the following attributes to ipasudorule: - - - order - - sudooption - - runasuser - - runasgroup - -It also fixes behavior of sudocmd assigned to the the sudorule, with the -adittion of the attributes: - - - allow_sudocmds - - deny_sudocmds - - allow_sudocmdgroups - - deny_sudocmdgroups - -README-sudorule and tests have been updated to comply with the changes. ---- - README-sudorule.md | 14 +- - ...sure-sudorule-does-not-have-sudooption.yml | 14 + - .../ensure-sudorule-has-sudooption.yml | 13 + - .../ensure-sudorule-is-present-with-order.yml | 12 + - .../sudorule/ensure-sudorule-is-present.yml | 2 + - .../ensure-sudorule-runasuser-is-absent.yml | 14 + - .../ensure-sudorule-runasuser-is-present.yml | 13 + - .../ensure-sudorule-sudocmd-is-absent.yml | 7 +- - .../ensure-sudorule-sudocmd-is-present.yml | 7 +- - plugins/modules/ipasudorule.py | 353 +++++++++++++----- - tests/sudorule/test_sudorule.yml | 204 +++++++--- - 11 files changed, 504 insertions(+), 149 deletions(-) - create mode 100644 playbooks/sudorule/ensure-sudorule-does-not-have-sudooption.yml - create mode 100644 playbooks/sudorule/ensure-sudorule-has-sudooption.yml - create mode 100644 playbooks/sudorule/ensure-sudorule-is-present-with-order.yml - create mode 100644 playbooks/sudorule/ensure-sudorule-runasuser-is-absent.yml - create mode 100644 playbooks/sudorule/ensure-sudorule-runasuser-is-present.yml - -diff --git a/README-sudorule.md b/README-sudorule.md -index bb3498b..50c73ad 100644 ---- a/README-sudorule.md -+++ b/README-sudorule.md -@@ -68,7 +68,7 @@ Example playbook to make sure sudocmds are present in Sudo Rule: - - ipasudorule: - ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -+ allow_sudocmd: - - /sbin/ifconfig - action: member - ``` -@@ -87,7 +87,7 @@ Example playbook to make sure sudocmds are not present in Sudo Rule: - - ipasudorule: - ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -+ allow_sudocmd: - - /sbin/ifconfig - action: member - state: absent -@@ -130,8 +130,14 @@ Variable | Description | Required - `hostgroup` | List of host group name strings assigned to this sudorule. | no - `user` | List of user name strings assigned to this sudorule. | no - `group` | List of user group name strings assigned to this sudorule. | no --`cmd` | List of sudocmd name strings assigned to this sudorule. | no --`cmdgroup` | List of sudocmd group name strings assigned wto this sudorule. | no -+`allow_sudocmd` | List of sudocmd name strings assigned to the allow group of this sudorule. | no -+`deny_sudocmd` | List of sudocmd name strings assigned to the deny group of this sudorule. | no -+`allow_sudocmdgroup` | List of sudocmd groups name strings assigned to the allow group of this sudorule. | no -+`deny_sudocmdgroup` | List of sudocmd groups name strings assigned to the deny group of this sudorule. | no -+`sudooption` \| `option` | List of options to the sudorule | no -+`order` | Integer to order the sudorule | no -+`runasuser` | List of users for Sudo to execute as. | no -+`runasgroup` | List of groups for Sudo to execute as. | no - `action` | Work on sudorule or member level. It can be on of `member` or `sudorule` and defaults to `sudorule`. | no - `state` | The state to ensure. It can be one of `present`, `absent`, `enabled` or `disabled`, default: `present`. | no - -diff --git a/playbooks/sudorule/ensure-sudorule-does-not-have-sudooption.yml b/playbooks/sudorule/ensure-sudorule-does-not-have-sudooption.yml -new file mode 100644 -index 0000000..1307044 ---- /dev/null -+++ b/playbooks/sudorule/ensure-sudorule-does-not-have-sudooption.yml -@@ -0,0 +1,14 @@ -+--- -+- name: Tests -+ hosts: ipaserver -+ become: true -+ gather_facts: false -+ -+ tasks: -+ # Ensure sudooption is absent in sudorule -+ - ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ sudooption: "!root" -+ action: member -+ state: absent -diff --git a/playbooks/sudorule/ensure-sudorule-has-sudooption.yml b/playbooks/sudorule/ensure-sudorule-has-sudooption.yml -new file mode 100644 -index 0000000..1f32b9a ---- /dev/null -+++ b/playbooks/sudorule/ensure-sudorule-has-sudooption.yml -@@ -0,0 +1,13 @@ -+--- -+- name: Tests -+ hosts: ipaserver -+ become: true -+ gather_facts: false -+ -+ tasks: -+ # Ensure sudooption is present in sudorule -+ - ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ sudooption: "!root" -+ action: member -diff --git a/playbooks/sudorule/ensure-sudorule-is-present-with-order.yml b/playbooks/sudorule/ensure-sudorule-is-present-with-order.yml -new file mode 100644 -index 0000000..9a3c2b2 ---- /dev/null -+++ b/playbooks/sudorule/ensure-sudorule-is-present-with-order.yml -@@ -0,0 +1,12 @@ -+--- -+- name: Tests -+ hosts: ipaserver -+ become: true -+ gather_facts: false -+ -+ tasks: -+ # Ensure sudorule is present with the given order. -+ - ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ order: 2 -diff --git a/playbooks/sudorule/ensure-sudorule-is-present.yml b/playbooks/sudorule/ensure-sudorule-is-present.yml -index 5b8f32b..89041af 100644 ---- a/playbooks/sudorule/ensure-sudorule-is-present.yml -+++ b/playbooks/sudorule/ensure-sudorule-is-present.yml -@@ -9,4 +9,6 @@ - ipaadmin_password: MyPassword123 - name: testrule1 - description: A test sudo rule. -+ allow_sudocmd: /bin/ls -+ deny_sudocmd: /bin/vim - state: present -diff --git a/playbooks/sudorule/ensure-sudorule-runasuser-is-absent.yml b/playbooks/sudorule/ensure-sudorule-runasuser-is-absent.yml -new file mode 100644 -index 0000000..56612f1 ---- /dev/null -+++ b/playbooks/sudorule/ensure-sudorule-runasuser-is-absent.yml -@@ -0,0 +1,14 @@ -+--- -+- name: Tests -+ hosts: ipaserver -+ become: true -+ gather_facts: false -+ -+ tasks: -+ # Ensure sudorule is present with the given order. -+ - ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ runasuser: admin -+ action: member -+ state: absent -diff --git a/playbooks/sudorule/ensure-sudorule-runasuser-is-present.yml b/playbooks/sudorule/ensure-sudorule-runasuser-is-present.yml -new file mode 100644 -index 0000000..8af49b9 ---- /dev/null -+++ b/playbooks/sudorule/ensure-sudorule-runasuser-is-present.yml -@@ -0,0 +1,13 @@ -+--- -+- name: Tests -+ hosts: ipaserver -+ become: true -+ gather_facts: false -+ -+ tasks: -+ # Ensure sudorule is present with the given order. -+ - ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ runasuser: admin -+ action: member -diff --git a/playbooks/sudorule/ensure-sudorule-sudocmd-is-absent.yml b/playbooks/sudorule/ensure-sudorule-sudocmd-is-absent.yml -index 942d0b5..328242a 100644 ---- a/playbooks/sudorule/ensure-sudorule-sudocmd-is-absent.yml -+++ b/playbooks/sudorule/ensure-sudorule-sudocmd-is-absent.yml -@@ -8,8 +8,13 @@ - - ipasudorule: - ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -+ allow_sudocmd: - - /sbin/ifconfig -+ deny_sudocmd: - - /usr/bin/vim -+ allow_sudocmdgroup: -+ - devops -+ deny_sudocmdgroup: -+ - users - action: member - state: absent -diff --git a/playbooks/sudorule/ensure-sudorule-sudocmd-is-present.yml b/playbooks/sudorule/ensure-sudorule-sudocmd-is-present.yml -index 61fcbb0..55acd61 100644 ---- a/playbooks/sudorule/ensure-sudorule-sudocmd-is-present.yml -+++ b/playbooks/sudorule/ensure-sudorule-sudocmd-is-present.yml -@@ -8,7 +8,12 @@ - - ipasudorule: - ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -+ allow_sudocmd: - - /sbin/ifconfig -+ deny_sudocmd: - - /usr/bin/vim -+ allow_sudocmdgroup: -+ - devops -+ deny_sudocmdgroup: -+ - users - action: member -diff --git a/plugins/modules/ipasudorule.py b/plugins/modules/ipasudorule.py -index c21f247..285a946 100644 ---- a/plugins/modules/ipasudorule.py -+++ b/plugins/modules/ipasudorule.py -@@ -79,18 +79,43 @@ - description: Host category the sudo rule applies to. - required: false - choices: ["all"] -- cmd: -- description: List of sudocmds assigned to this sudorule. -+ allow_sudocmd: -+ description: List of allowed sudocmds assigned to this sudorule. - required: false - type: list -- cmdgroup: -- description: List of sudocmd groups assigned to this sudorule. -+ allow_sudocmdgroup: -+ description: List of allowed sudocmd groups assigned to this sudorule. -+ required: false -+ type: list -+ deny_sudocmd: -+ description: List of denied sudocmds assigned to this sudorule. -+ required: false -+ type: list -+ deny_sudocmdgroup: -+ description: List of denied sudocmd groups assigned to this sudorule. - required: false - type: list - cmdcategory: -- description: Cammand category the sudo rule applies to -+ description: Command category the sudo rule applies to - required: false - choices: ["all"] -+ order: -+ description: Order to apply this rule. -+ required: false -+ type: int -+ sudooption: -+ description: -+ required: false -+ type: list -+ aliases: ["options"] -+ runasuser: -+ description: List of users for Sudo to execute as. -+ required: false -+ type: list -+ runasgroup: -+ description: List of groups for Sudo to execute as. -+ required: false -+ type: list - action: - description: Work on sudorule or member level - default: sudorule -@@ -111,13 +136,13 @@ - - # Ensure sudocmd is present in Sudo Rule - - ipasudorule: -- ipaadmin_password: pass1234 -- name: testrule1 -- cmd: -- - /sbin/ifconfig -- - /usr/bin/vim -- action: member -- state: absent -+ ipaadmin_password: pass1234 -+ name: testrule1 -+ allow_sudocmd: -+ - /sbin/ifconfig -+ - /usr/bin/vim -+ action: member -+ state: absent - - # Ensure host server is present in Sudo Rule - - ipasudorule: -@@ -160,7 +185,7 @@ - from ansible.module_utils.basic import AnsibleModule - from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ -- module_params_get -+ module_params_get, gen_add_del_lists - - - def find_sudorule(module, name): -@@ -180,14 +205,26 @@ def find_sudorule(module, name): - return None - - --def gen_args(ansible_module): -- arglist = ['description', 'usercategory', 'hostcategory', 'cmdcategory', -- 'runasusercategory', 'runasgroupcategory', 'nomembers'] -+def gen_args(description, usercat, hostcat, cmdcat, runasusercat, -+ runasgroupcat, order, nomembers): - _args = {} -- for arg in arglist: -- value = module_params_get(ansible_module, arg) -- if value is not None: -- _args[arg] = value -+ -+ if description is not None: -+ _args['description'] = description -+ if usercat is not None: -+ _args['usercategory'] = usercat -+ if hostcat is not None: -+ _args['hostcategory'] = hostcat -+ if cmdcat is not None: -+ _args['cmdcategory'] = cmdcat -+ if runasusercat is not None: -+ _args['ipasudorunasusercategory'] = runasusercat -+ if runasgroupcat is not None: -+ _args['ipasudorunasgroupcategory'] = runasgroupcat -+ if order is not None: -+ _args['sudoorder'] = order -+ if nomembers is not None: -+ _args['nomembers'] = nomembers - - return _args - -@@ -212,13 +249,21 @@ def main(): - hostgroup=dict(required=False, type='list', default=None), - user=dict(required=False, type='list', default=None), - group=dict(required=False, type='list', default=None), -- cmd=dict(required=False, type="list", default=None), -+ allow_sudocmd=dict(required=False, type="list", default=None), -+ deny_sudocmd=dict(required=False, type="list", default=None), -+ allow_sudocmdgroup=dict(required=False, type="list", default=None), -+ deny_sudocmdgroup=dict(required=False, type="list", default=None), - cmdcategory=dict(required=False, type="str", default=None, - choices=["all"]), - runasusercategory=dict(required=False, type="str", default=None, - choices=["all"]), - runasgroupcategory=dict(required=False, type="str", default=None, - choices=["all"]), -+ runasuser=dict(required=False, type="list", default=None), -+ runasgroup=dict(required=False, type="list", default=None), -+ order=dict(type="int", required=False, aliases=['sudoorder']), -+ sudooption=dict(required=False, type='list', default=None, -+ aliases=["options"]), - action=dict(type="str", default="sudorule", - choices=["member", "sudorule"]), - # state -@@ -256,8 +301,16 @@ def main(): - hostgroup = module_params_get(ansible_module, "hostgroup") - user = module_params_get(ansible_module, "user") - group = module_params_get(ansible_module, "group") -- cmd = module_params_get(ansible_module, 'cmd') -- cmdgroup = module_params_get(ansible_module, 'cmdgroup') -+ allow_sudocmd = module_params_get(ansible_module, 'allow_sudocmd') -+ allow_sudocmdgroup = module_params_get(ansible_module, -+ 'allow_sudocmdgroup') -+ deny_sudocmd = module_params_get(ansible_module, 'deny_sudocmd') -+ deny_sudocmdgroup = module_params_get(ansible_module, -+ 'deny_sudocmdgroup') -+ sudooption = module_params_get(ansible_module, "sudooption") -+ order = module_params_get(ansible_module, "order") -+ runasuser = module_params_get(ansible_module, "runasuser") -+ runasgroup = module_params_get(ansible_module, "runasgroup") - action = module_params_get(ansible_module, "action") - - # state -@@ -272,28 +325,30 @@ def main(): - if action == "member": - invalid = ["description", "usercategory", "hostcategory", - "cmdcategory", "runasusercategory", -- "runasgroupcategory", "nomembers"] -+ "runasgroupcategory", "order", "nomembers"] - -- for x in invalid: -- if x in vars() and vars()[x] is not None: -+ for arg in invalid: -+ if arg in vars() and vars()[arg] is not None: - ansible_module.fail_json( - msg="Argument '%s' can not be used with action " -- "'%s'" % (x, action)) -+ "'%s'" % (arg, action)) - - elif state == "absent": - if len(names) < 1: - ansible_module.fail_json(msg="No name given.") - invalid = ["description", "usercategory", "hostcategory", - "cmdcategory", "runasusercategory", -- "runasgroupcategory", "nomembers"] -+ "runasgroupcategory", "nomembers", "order"] - if action == "sudorule": - invalid.extend(["host", "hostgroup", "user", "group", -- "cmd", "cmdgroup"]) -- for x in invalid: -- if vars()[x] is not None: -+ "runasuser", "runasgroup", "allow_sudocmd", -+ "allow_sudocmdgroup", "deny_sudocmd", -+ "deny_sudocmdgroup", "sudooption"]) -+ for arg in invalid: -+ if vars()[arg] is not None: - ansible_module.fail_json( - msg="Argument '%s' can not be used with state '%s'" % -- (x, state)) -+ (arg, state)) - - elif state in ["enabled", "disabled"]: - if len(names) < 1: -@@ -305,12 +360,14 @@ def main(): - invalid = ["description", "usercategory", "hostcategory", - "cmdcategory", "runasusercategory", "runasgroupcategory", - "nomembers", "nomembers", "host", "hostgroup", -- "user", "group", "cmd", "cmdgroup"] -- for x in invalid: -- if vars()[x] is not None: -+ "user", "group", "allow_sudocmd", "allow_sudocmdgroup", -+ "deny_sudocmd", "deny_sudocmdgroup", "runasuser", -+ "runasgroup", "order", "sudooption"] -+ for arg in invalid: -+ if vars()[arg] is not None: - ansible_module.fail_json( - msg="Argument '%s' can not be used with state '%s'" % -- (x, state)) -+ (arg, state)) - else: - ansible_module.fail_json(msg="Invalid state '%s'" % state) - -@@ -335,7 +392,9 @@ def main(): - # Create command - if state == "present": - # Generate args -- args = gen_args(ansible_module) -+ args = gen_args(description, usercategory, hostcategory, -+ cmdcategory, runasusercategory, -+ runasgroupcategory, order, nomembers) - if action == "sudorule": - # Found the sudorule - if res_find is not None: -@@ -351,44 +410,42 @@ def main(): - res_find = {} - - # Generate addition and removal lists -- host_add = list( -- set(host or []) - -- set(res_find.get("member_host", []))) -- host_del = list( -- set(res_find.get("member_host", [])) - -- set(host or [])) -- hostgroup_add = list( -- set(hostgroup or []) - -- set(res_find.get("member_hostgroup", []))) -- hostgroup_del = list( -- set(res_find.get("member_hostgroup", [])) - -- set(hostgroup or [])) -- -- user_add = list( -- set(user or []) - -- set(res_find.get("member_user", []))) -- user_del = list( -- set(res_find.get("member_user", [])) - -- set(user or [])) -- group_add = list( -- set(group or []) - -- set(res_find.get("member_group", []))) -- group_del = list( -- set(res_find.get("member_group", [])) - -- set(group or [])) -- -- cmd_add = list( -- set(cmd or []) - -- set(res_find.get("member_cmd", []))) -- cmd_del = list( -- set(res_find.get("member_cmd", [])) - -- set(cmd or [])) -- cmdgroup_add = list( -- set(cmdgroup or []) - -- set(res_find.get("member_cmdgroup", []))) -- cmdgroup_del = list( -- set(res_find.get("member_cmdgroup", [])) - -- set(cmdgroup or [])) -+ host_add, host_del = gen_add_del_lists( -+ host, res_find.get('member_host', [])) -+ -+ hostgroup_add, hostgroup_del = gen_add_del_lists( -+ hostgroup, res_find.get('member_hostgroup', [])) -+ -+ user_add, user_del = gen_add_del_lists( -+ user, res_find.get('member_user', [])) -+ -+ group_add, group_del = gen_add_del_lists( -+ group, res_find.get('member_group', [])) -+ -+ allow_cmd_add, allow_cmd_del = gen_add_del_lists( -+ allow_sudocmd, -+ res_find.get('memberallowcmd_sudocmd', [])) -+ -+ allow_cmdgroup_add, allow_cmdgroup_del = gen_add_del_lists( -+ allow_sudocmdgroup, -+ res_find.get('memberallowcmd_sudocmdgroup', [])) -+ -+ deny_cmd_add, deny_cmd_del = gen_add_del_lists( -+ deny_sudocmd, -+ res_find.get('memberdenycmd_sudocmd', [])) -+ -+ deny_cmdgroup_add, deny_cmdgroup_del = gen_add_del_lists( -+ deny_sudocmdgroup, -+ res_find.get('memberdenycmd_sudocmdgroup', [])) -+ -+ sudooption_add, sudooption_del = gen_add_del_lists( -+ sudooption, res_find.get('ipasudoopt', [])) -+ -+ runasuser_add, runasuser_del = gen_add_del_lists( -+ runasuser, res_find.get('ipasudorunas_user', [])) -+ -+ runasgroup_add, runasgroup_del = gen_add_del_lists( -+ runasgroup, res_find.get('ipasudorunas_group', [])) - - # Add hosts and hostgroups - if len(host_add) > 0 or len(hostgroup_add) > 0: -@@ -420,20 +477,59 @@ def main(): - "group": group_del, - }]) - -- # Add commands -- if len(cmd_add) > 0 or len(cmdgroup_add) > 0: -+ # Add commands allowed -+ if len(allow_cmd_add) > 0 or len(allow_cmdgroup_add) > 0: - commands.append([name, "sudorule_add_allow_command", -- { -- "sudocmd": cmd_add, -- "sudocmdgroup": cmdgroup_add, -- }]) -- -- if len(cmd_del) > 0 or len(cmdgroup_del) > 0: -+ {"sudocmd": allow_cmd_add, -+ "sudocmdgroup": allow_cmdgroup_add, -+ }]) -+ -+ if len(allow_cmd_del) > 0 or len(allow_cmdgroup_del) > 0: -+ commands.append([name, "sudorule_remove_allow_command", -+ {"sudocmd": allow_cmd_del, -+ "sudocmdgroup": allow_cmdgroup_del -+ }]) -+ -+ # Add commands denied -+ if len(deny_cmd_add) > 0 or len(deny_cmdgroup_add) > 0: - commands.append([name, "sudorule_add_deny_command", -- { -- "sudocmd": cmd_del, -- "sudocmdgroup": cmdgroup_del -- }]) -+ {"sudocmd": deny_cmd_add, -+ "sudocmdgroup": deny_cmdgroup_add, -+ }]) -+ -+ if len(deny_cmd_del) > 0 or len(deny_cmdgroup_del) > 0: -+ commands.append([name, "sudorule_remove_deny_command", -+ {"sudocmd": deny_cmd_del, -+ "sudocmdgroup": deny_cmdgroup_del -+ }]) -+ -+ # Add RunAS Users -+ if len(runasuser_add) > 0: -+ commands.append([name, "sudorule_add_runasuser", -+ {"user": runasuser_add}]) -+ # Remove RunAS Users -+ if len(runasuser_del) > 0: -+ commands.append([name, "sudorule_remove_runasuser", -+ {"user": runasuser_del}]) -+ -+ # Add RunAS Groups -+ if len(runasgroup_add) > 0: -+ commands.append([name, "sudorule_add_runasgroup", -+ {"group": runasgroup_add}]) -+ # Remove RunAS Groups -+ if len(runasgroup_del) > 0: -+ commands.append([name, "sudorule_remove_runasgroup", -+ {"group": runasgroup_del}]) -+ -+ # Add sudo options -+ for sudoopt in sudooption_add: -+ commands.append([name, "sudorule_add_option", -+ {"ipasudoopt": sudoopt}]) -+ -+ # Remove sudo options -+ for sudoopt in sudooption_del: -+ commands.append([name, "sudorule_remove_option", -+ {"ipasudoopt": sudoopt}]) - - elif action == "member": - if res_find is None: -@@ -456,11 +552,38 @@ def main(): - }]) - - # Add commands -- if cmd is not None: -+ if allow_sudocmd is not None \ -+ or allow_sudocmdgroup is not None: - commands.append([name, "sudorule_add_allow_command", -- { -- "sudocmd": cmd, -- }]) -+ {"sudocmd": allow_sudocmd, -+ "sudocmdgroup": allow_sudocmdgroup, -+ }]) -+ -+ # Add commands -+ if deny_sudocmd is not None \ -+ or deny_sudocmdgroup is not None: -+ commands.append([name, "sudorule_add_deny_command", -+ {"sudocmd": deny_sudocmd, -+ "sudocmdgroup": deny_sudocmdgroup, -+ }]) -+ -+ # Add RunAS Users -+ if runasuser is not None: -+ commands.append([name, "sudorule_add_runasuser", -+ {"user": runasuser}]) -+ -+ # Add RunAS Groups -+ if runasgroup is not None: -+ commands.append([name, "sudorule_add_runasgroup", -+ {"group": runasgroup}]) -+ -+ # Add options -+ if sudooption is not None: -+ existing_opts = res_find.get('ipasudoopt', []) -+ for sudoopt in sudooption: -+ if sudoopt not in existing_opts: -+ commands.append([name, "sudorule_add_option", -+ {"ipasudoopt": sudoopt}]) - - elif state == "absent": - if action == "sudorule": -@@ -487,12 +610,40 @@ def main(): - "group": group, - }]) - -- # Remove commands -- if cmd is not None: -- commands.append([name, "sudorule_add_deny_command", -- { -- "sudocmd": cmd, -- }]) -+ # Remove allow commands -+ if allow_sudocmd is not None \ -+ or allow_sudocmdgroup is not None: -+ commands.append([name, "sudorule_remove_allow_command", -+ {"sudocmd": allow_sudocmd, -+ "sudocmdgroup": allow_sudocmdgroup -+ }]) -+ -+ # Remove deny commands -+ if deny_sudocmd is not None \ -+ or deny_sudocmdgroup is not None: -+ commands.append([name, "sudorule_remove_deny_command", -+ {"sudocmd": deny_sudocmd, -+ "sudocmdgroup": deny_sudocmdgroup -+ }]) -+ -+ # Remove RunAS Users -+ if runasuser is not None: -+ commands.append([name, "sudorule_remove_runasuser", -+ {"user": runasuser}]) -+ -+ # Remove RunAS Groups -+ if runasgroup is not None: -+ commands.append([name, "sudorule_remove_runasgroup", -+ {"group": runasgroup}]) -+ -+ # Remove options -+ if sudooption is not None: -+ existing_opts = res_find.get('ipasudoopt', []) -+ for sudoopt in sudooption: -+ if sudoopt in existing_opts: -+ commands.append([name, -+ "sudorule_remove_option", -+ {"ipasudoopt": sudoopt}]) - - elif state == "enabled": - if res_find is None: -@@ -530,9 +681,9 @@ def main(): - changed = True - else: - changed = True -- except Exception as e: -+ except Exception as ex: - ansible_module.fail_json(msg="%s: %s: %s" % (command, name, -- str(e))) -+ str(ex))) - # Get all errors - # All "already a member" and "not a member" failures in the - # result are ignored. All others are reported. -@@ -549,8 +700,8 @@ def main(): - if len(errors) > 0: - ansible_module.fail_json(msg=", ".join(errors)) - -- except Exception as e: -- ansible_module.fail_json(msg=str(e)) -+ except Exception as ex: -+ ansible_module.fail_json(msg=str(ex)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) -diff --git a/tests/sudorule/test_sudorule.yml b/tests/sudorule/test_sudorule.yml -index 88ed90a..25090bb 100644 ---- a/tests/sudorule/test_sudorule.yml -+++ b/tests/sudorule/test_sudorule.yml -@@ -16,15 +16,22 @@ - - - name: Ensure some sudocmds are available - ipasudocmd: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: - - /sbin/ifconfig - - /usr/bin/vim - state: present - -+ - name: Ensure sudocmdgroup is available -+ ipasudocmdgroup: -+ ipaadmin_password: MyPassword123 -+ name: test_sudorule -+ sudocmd: /usr/bin/vim -+ state: present -+ - - name: Ensure sudorules are absent - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: - - testrule1 - - allusers -@@ -34,21 +41,21 @@ - - - name: Ensure sudorule is present - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - register: result - failed_when: not result.changed - - - name: Ensure sudorule is present again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - register: result - failed_when: result.changed - - - name: Ensure sudorule is present, runAsUserCategory. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - runAsUserCategory: all - register: result -@@ -56,7 +63,7 @@ - - - name: Ensure sudorule is present, with usercategory 'all' - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allusers - usercategory: all - register: result -@@ -64,7 +71,7 @@ - - - name: Ensure sudorule is present, with usercategory 'all', again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allusers - usercategory: all - register: result -@@ -72,7 +79,7 @@ - - - name: Ensure sudorule is present, with hostategory 'all' - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allhosts - hostcategory: all - register: result -@@ -80,7 +87,7 @@ - - - name: Ensure sudorule is present, with hostategory 'all', again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allhosts - hostcategory: all - register: result -@@ -88,13 +95,13 @@ - - - name: Ensure sudorule is disabled - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - state: disabled - - - name: Ensure sudorule is disabled, again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - state: disabled - register: result -@@ -102,7 +109,7 @@ - - - name: Ensure sudorule is enabled - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - state: enabled - register: result -@@ -110,37 +117,77 @@ - - - name: Ensure sudorule is enabled, again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - state: enabled - register: result - failed_when: result.changed - -- - name: Ensure sudorule is present and some sudocmd are a member of it. -+ - name: Ensure sudorule is present and some sudocmd are allowed. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -+ allow_sudocmd: - - /sbin/ifconfig -- - /usr/bin/vim - action: member - register: result - failed_when: not result.changed - -- - name: Ensure sudorule is present and some sudocmd are a member of it, again. -+ - name: Ensure sudorule is present and some sudocmd are allowed, again. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -+ allow_sudocmd: - - /sbin/ifconfig -+ action: member -+ register: result -+ failed_when: result.changed -+ -+ - name: Ensure sudorule is present and some sudocmd are denyed. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ deny_sudocmd: -+ - /usr/bin/vim -+ action: member -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure sudorule is present and some sudocmd are denyed, again. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ deny_sudocmd: - - /usr/bin/vim - action: member - register: result - failed_when: result.changed - -+ - name: Ensure sudorule is present and, sudocmds are absent. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ allow_sudocmd: /sbin/ifconfig -+ deny_sudocmd: /usr/bin/vim -+ action: member -+ state: absent -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure sudorule is present and, sudocmds are absent, again. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ allow_sudocmd: /sbin/ifconfig -+ deny_sudocmd: /usr/bin/vim -+ action: member -+ state: absent -+ register: result -+ failed_when: result.changed -+ - - name: Ensure sudorule is present with cmdcategory 'all'. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allcommands - cmdcategory: all - register: result -@@ -148,7 +195,7 @@ - - - name: Ensure sudorule is present with cmdcategory 'all', again. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allcommands - cmdcategory: all - register: result -@@ -156,7 +203,7 @@ - - - name: Ensure host "{{ groups.ipaserver[0] }}" is present in sudorule. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - host: "{{ groups.ipaserver[0] }}" - action: member -@@ -165,7 +212,7 @@ - - - name: Ensure host "{{ groups.ipaserver[0] }}" is present in sudorule, again. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - host: "{{ groups.ipaserver[0] }}" - action: member -@@ -190,25 +237,77 @@ - register: result - failed_when: result.changed - -- - name: Ensure sudorule sudocmds are absent -+ - name: Ensure sudorule is present, with an allow_sudocmdgroup. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -- - /sbin/ifconfig -- - /usr/bin/vim -+ allow_sudocmdgroup: test_sudorule -+ state: present -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure sudorule is present, with an allow_sudocmdgroup, again. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ allow_sudocmdgroup: test_sudorule -+ state: present -+ register: result -+ failed_when: result.changed -+ -+ - name: Ensure sudorule is present, but allow_sudocmdgroup is absent. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ allow_sudocmdgroup: test_sudorule - action: member - state: absent - register: result - failed_when: not result.changed - -- - name: Ensure sudorule sudocmds are absent, again -+ - name: Ensure sudorule is present, but allow_sudocmdgroup is absent. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 -- cmd: -- - /sbin/ifconfig -- - /usr/bin/vim -+ allow_sudocmdgroup: test_sudorule -+ action: member -+ state: absent -+ register: result -+ failed_when: result.changed -+ -+ - name: Ensure sudorule is present, with an deny_sudocmdgroup. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ deny_sudocmdgroup: test_sudorule -+ state: present -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure sudorule is present, with an deny_sudocmdgroup, again. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ deny_sudocmdgroup: test_sudorule -+ state: present -+ register: result -+ failed_when: result.changed -+ -+ - name: Ensure sudorule is present, but deny_sudocmdgroup is absent. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ deny_sudocmdgroup: test_sudorule -+ action: member -+ state: absent -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure sudorule is present, but deny_sudocmdgroup is absent, again. -+ ipasudorule: -+ ipaadmin_password: MyPassword123 -+ name: testrule1 -+ deny_sudocmdgroup: test_sudorule - action: member - state: absent - register: result -@@ -216,7 +315,7 @@ - - - name: Ensure sudorule is absent - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - state: absent - register: result -@@ -224,7 +323,7 @@ - - - name: Ensure sudorule is absent, again. - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: testrule1 - state: absent - register: result -@@ -232,7 +331,7 @@ - - - name: Ensure sudorule allhosts is absent - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allhosts - state: absent - register: result -@@ -240,7 +339,7 @@ - - - name: Ensure sudorule allhosts is absent, again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allhosts - state: absent - register: result -@@ -248,7 +347,7 @@ - - - name: Ensure sudorule allusers is absent - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allusers - state: absent - register: result -@@ -256,7 +355,7 @@ - - - name: Ensure sudorule allusers is absent, again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allusers - state: absent - register: result -@@ -264,7 +363,7 @@ - - - name: Ensure sudorule allcommands is absent - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allcommands - state: absent - register: result -@@ -272,8 +371,29 @@ - - - name: Ensure sudorule allcommands is absent, again - ipasudorule: -- ipaadmin_password: pass1234 -+ ipaadmin_password: MyPassword123 - name: allcommands - state: absent - register: result - failed_when: result.changed -+ -+ # cleanup -+ - name : Ensure sudocmdgroup is absent -+ ipasudocmdgroup: -+ ipaadmin_password: MyPassword123 -+ name: test_sudorule -+ state: absent -+ -+ - name: Ensure hostgroup is absent. -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: cluster -+ state: absent -+ -+ - name: Ensure sudocmds are absent -+ ipasudocmd: -+ ipaadmin_password: MyPassword123 -+ name: -+ - /sbin/ifconfig -+ - /usr/bin/vim -+ state: absent diff --git a/SOURCES/ansible-freeipa-0.1.8-ansible_freeipa_module-Fix-comparison-of-bool-parameters-in-compare_args_ipa_rhbz#1784514.patch b/SOURCES/ansible-freeipa-0.1.8-ansible_freeipa_module-Fix-comparison-of-bool-parameters-in-compare_args_ipa_rhbz#1784514.patch deleted file mode 100644 index 767f487..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ansible_freeipa_module-Fix-comparison-of-bool-parameters-in-compare_args_ipa_rhbz#1784514.patch +++ /dev/null @@ -1,177 +0,0 @@ -From 3780a9a00e77ae0fd2944b36adad446d094fc90f Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Tue, 11 Feb 2020 10:34:39 +0100 -Subject: [PATCH] ansible_freeipa_module: Fix comparison of bool parameters in - compare_args_ipa - -Bool types are not iterable. Therefore the comparison using sets was failing -with a TypeError. This prevented to change the bool parameters for hosts. - -A test for the host module has been added to verify that the bool parameters -can be modified. - -New test: - - tests/host/test_host_bool_params.yml - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1784514 ---- - .../module_utils/ansible_freeipa_module.py | 18 ++- - tests/host/test_host_bool_params.yml | 119 ++++++++++++++++++ - 2 files changed, 133 insertions(+), 4 deletions(-) - create mode 100644 tests/host/test_host_bool_params.yml - -diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py -index 8154a12..9e97b88 100644 ---- a/plugins/module_utils/ansible_freeipa_module.py -+++ b/plugins/module_utils/ansible_freeipa_module.py -@@ -222,10 +222,20 @@ def compare_args_ipa(module, args, ipa): - arg = [to_text(_arg) for _arg in arg] - if isinstance(ipa_arg[0], unicode) and isinstance(arg[0], int): - arg = [to_text(_arg) for _arg in arg] -- # module.warn("%s <=> %s" % (arg, ipa_arg)) -- if set(arg) != set(ipa_arg): -- # module.warn("DIFFERENT") -- return False -+ # module.warn("%s <=> %s" % (repr(arg), repr(ipa_arg))) -+ try: -+ arg_set = set(arg) -+ ipa_arg_set = set(ipa_arg) -+ except TypeError: -+ if arg != ipa_arg: -+ # module.warn("%s != %s" % (repr(arg), repr(ipa_arg))) -+ return False -+ else: -+ if arg_set != ipa_arg_set: -+ # module.warn("%s != %s" % (repr(arg), repr(ipa_arg))) -+ return False -+ -+ # module.warn("%s == %s" % (repr(arg), repr(ipa_arg))) - - return True - -diff --git a/tests/host/test_host_bool_params.yml b/tests/host/test_host_bool_params.yml -new file mode 100644 -index 0000000..824ea99 ---- /dev/null -+++ b/tests/host/test_host_bool_params.yml -@@ -0,0 +1,119 @@ -+--- -+- name: Test host bool parameters -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ - name: Get Domain from server name -+ set_fact: -+ ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}" -+ when: ipaserver_domain is not defined -+ -+ - name: Set host1_fqdn .. host6_fqdn -+ set_fact: -+ host1_fqdn: "{{ 'host1.' + ipaserver_domain }}" -+ -+ - name: Host absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: -+ - "{{ host1_fqdn }}" -+ update_dns: yes -+ state: absent -+ -+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ force: yes -+ requires_pre_auth: yes -+ ok_as_delegate: yes -+ ok_to_auth_as_delegate: yes -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ requires_pre_auth: yes -+ ok_as_delegate: yes -+ ok_to_auth_as_delegate: yes -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate set to no -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ requires_pre_auth: no -+ ok_as_delegate: no -+ ok_to_auth_as_delegate: no -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate set to no again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ requires_pre_auth: no -+ ok_as_delegate: no -+ ok_to_auth_as_delegate: no -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ requires_pre_auth: yes -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ requires_pre_auth: yes -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with ok_as_delegate -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ok_as_delegate: yes -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with ok_as_delegate again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ok_as_delegate: yes -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with ok_to_auth_as_delegate -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ok_to_auth_as_delegate: yes -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present with ok_to_auth_as_delegate again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ok_to_auth_as_delegate: yes -+ register: result -+ failed_when: result.changed -+ -+ - name: Host absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: -+ - "{{ host1_fqdn }}" -+ update_dns: yes -+ state: absent diff --git a/SOURCES/ansible-freeipa-0.1.8-ipahbacrule-Fix-handing-of-members-with-action-hbacrule_rhbz#1787996.patch b/SOURCES/ansible-freeipa-0.1.8-ipahbacrule-Fix-handing-of-members-with-action-hbacrule_rhbz#1787996.patch deleted file mode 100644 index 9e04d44..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipahbacrule-Fix-handing-of-members-with-action-hbacrule_rhbz#1787996.patch +++ /dev/null @@ -1,838 +0,0 @@ -From 3865ce657e3ea1b621aa054c792201aedfde2d11 Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Fri, 7 Feb 2020 10:11:38 +0100 -Subject: [PATCH] ipahbacrule: Fix handing of members with action hbacrule - -Changing members (host, hostgroup, hbacsvc, hbacsvcgroup, user, group) with -action hbacrule was not working due to the use of the wrong parameter -prefix. This has been fixed and the old members are removed correctly now. - -The test script has been reworked completely to verify the fix. - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1787996 ---- - plugins/modules/ipahbacrule.py | 24 +- - tests/hbacrule/test_hbacrule.yml | 549 +++++++++++++++++++++++-------- - 2 files changed, 432 insertions(+), 141 deletions(-) - -diff --git a/plugins/modules/ipahbacrule.py b/plugins/modules/ipahbacrule.py -index 385876b..82340c2 100644 ---- a/plugins/modules/ipahbacrule.py -+++ b/plugins/modules/ipahbacrule.py -@@ -344,41 +344,41 @@ def main(): - # Generate addition and removal lists - host_add = list( - set(host or []) - -- set(res_find.get("member_host", []))) -+ set(res_find.get("memberhost_host", []))) - host_del = list( -- set(res_find.get("member_host", [])) - -+ set(res_find.get("memberhost_host", [])) - - set(host or [])) - hostgroup_add = list( - set(hostgroup or []) - -- set(res_find.get("member_hostgroup", []))) -+ set(res_find.get("memberhost_hostgroup", []))) - hostgroup_del = list( -- set(res_find.get("member_hostgroup", [])) - -+ set(res_find.get("memberhost_hostgroup", [])) - - set(hostgroup or [])) - - hbacsvc_add = list( - set(hbacsvc or []) - -- set(res_find.get("member_hbacsvc", []))) -+ set(res_find.get("memberservice_hbacsvc", []))) - hbacsvc_del = list( -- set(res_find.get("member_hbacsvc", [])) - -+ set(res_find.get("memberservice_hbacsvc", [])) - - set(hbacsvc or [])) - hbacsvcgroup_add = list( - set(hbacsvcgroup or []) - -- set(res_find.get("member_hbacsvcgroup", []))) -+ set(res_find.get("memberservice_hbacsvcgroup", []))) - hbacsvcgroup_del = list( -- set(res_find.get("member_hbacsvcgroup", [])) - -+ set(res_find.get("memberservice_hbacsvcgroup", [])) - - set(hbacsvcgroup or [])) - - user_add = list( - set(user or []) - -- set(res_find.get("member_user", []))) -+ set(res_find.get("memberuser_user", []))) - user_del = list( -- set(res_find.get("member_user", [])) - -+ set(res_find.get("memberuser_user", [])) - - set(user or [])) - group_add = list( - set(group or []) - -- set(res_find.get("member_group", []))) -+ set(res_find.get("memberuser_group", []))) - group_del = list( -- set(res_find.get("member_group", [])) - -+ set(res_find.get("memberuser_group", [])) - - set(group or [])) - - # Add hosts and hostgroups -diff --git a/tests/hbacrule/test_hbacrule.yml b/tests/hbacrule/test_hbacrule.yml -index a5615cc..38858d3 100644 ---- a/tests/hbacrule/test_hbacrule.yml -+++ b/tests/hbacrule/test_hbacrule.yml -@@ -1,338 +1,629 @@ - --- --- name: Tests -+- name: Playbook to handle hbacrules - hosts: ipaserver - become: true -- gather_facts: false - - tasks: -- - name: Ensure HBAC Rule allhosts is absent -- ipahbacrule: -+ - name: Get Domain from server name -+ set_fact: -+ ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}" -+ when: ipaserver_domain is not defined -+ -+ # CLEANUP TEST ITEMS -+ -+ - name: Ensure test hosts are absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ - "{{ 'testhost03.' + ipaserver_domain }}" -+ - "{{ 'testhost04.' + ipaserver_domain }}" -+ state: absent -+ -+ - name: Ensure test hostgroups are absent -+ ipahostgroup: - ipaadmin_password: MyPassword123 -- name: allhosts,sshd-pinky,loginRule -+ name: testhostgroup01,testhostgroup02,testhostgroup03,testhostgroup04 - state: absent - -- - name: User pinky absent -+ - name: Ensure test users are absent - ipauser: - ipaadmin_password: MyPassword123 -- name: pinky -+ name: testuser01,testuser02,testuser03,testuser04 - state: absent - -- - name: User group login absent -+ - name: Ensure test user groups are absent - ipagroup: - ipaadmin_password: MyPassword123 -- name: login -+ name: testgroup01,testgroup02,testgroup03,testgroup04 -+ state: absent -+ -+ - name: Ensure test HBAC Services are absent -+ ipahbacsvc: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvc01,testhbacsvc02,testhbacsvc03,testhbacsvc04 -+ state: absent -+ -+ - name: Ensure test HBAC Service Groups are absent -+ ipahbacsvcgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvcgroup01,testhbacsvcgroup02,testhbacsvcgroup03,testhbacsvcgroup04 - state: absent - -- - name: User pinky present -+ # CREATE TEST ITEMS -+ -+ - name: Ensure hosts "{{ 'host[1..4].' + ipaserver_domain }}" are present -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ 'testhost01.' + ipaserver_domain }}" -+ force: yes -+ - name: "{{ 'testhost02.' + ipaserver_domain }}" -+ force: yes -+ - name: "{{ 'testhost03.' + ipaserver_domain }}" -+ force: yes -+ - name: "{{ 'testhost04.' + ipaserver_domain }}" -+ force: yes -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure host-group testhostgroup01 is present -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhostgroup01 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure host-group testhostgroup02 is present -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhostgroup02 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure host-group testhostgroup03 is present -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhostgroup03 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure host-group testhostgroup04 is present -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhostgroup04 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure testusers are present - ipauser: - ipaadmin_password: MyPassword123 -- name: pinky -- uid: 10001 -- gid: 100 -- phone: "+555123457" -- email: pinky@acme.com -- principalexpiration: "20220119235959" -- #passwordexpiration: "2022-01-19 23:59:59" -- first: pinky -- last: Acme -+ users: -+ - name: testuser01 -+ first: test -+ last: user01 -+ - name: testuser02 -+ first: test -+ last: user02 -+ - name: testuser03 -+ first: test -+ last: user03 -+ - name: testuser04 -+ first: test -+ last: user04 - register: result - failed_when: not result.changed - -- - name: User group login present -+ - name: Ensure user group testgroup01 is present - ipagroup: - ipaadmin_password: MyPassword123 -- name: login -+ name: testgroup01 - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule allhosts is present -- ipahbacrule: -+ - name: Ensure user group testgroup02 is present -+ ipagroup: - ipaadmin_password: MyPassword123 -- name: allhosts -- usercategory: all -+ name: testgroup02 - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule allhosts is present again -- ipahbacrule: -+ - name: Ensure user group testgroup03 is present -+ ipagroup: - ipaadmin_password: MyPassword123 -- name: allhosts -- usercategory: all -+ name: testgroup03 - register: result -- failed_when: result.changed -+ failed_when: not result.changed - -- - name: Ensure host "{{ groups.ipaserver[0] }}" is present in HBAC Rule allhosts -+ - name: Ensure user group testgroup04 is present -+ ipagroup: -+ ipaadmin_password: MyPassword123 -+ name: testgroup04 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service testhbacsvc01 is present -+ ipahbacsvc: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvc01 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service testhbacsvc02 is present -+ ipahbacsvc: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvc02 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service testhbacsvc03 is present -+ ipahbacsvc: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvc03 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service testhbacsvc04 is present -+ ipahbacsvc: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvc04 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service Group testhbacsvcgroup01 is present -+ ipahbacsvcgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvcgroup01 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service Group testhbacsvcgroup02 is present -+ ipahbacsvcgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvcgroup02 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service Group testhbacsvcgroup03 is present -+ ipahbacsvcgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvcgroup03 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC Service Group testhbacsvcgroup04 is present -+ ipahbacsvcgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvcgroup04 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure test HBAC rule hbacrule01 is absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: allhosts -- host: "{{ groups.ipaserver[0] }}" -- action: member -+ name: hbacrule01 -+ state: absent -+ -+ # ENSURE HBACRULE -+ -+ - name: Ensure HBAC rule hbacrule01 is present -+ ipahbacrule: -+ ipaadmin_password: MyPassword123 -+ name: hbacrule01 - register: result - failed_when: not result.changed - -- - name: Ensure host "{{ groups.ipaserver[0] }}" is present in HBAC Rule allhosts again -+ - name: Ensure HBAC rule hbacrule01 is present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: allhosts -- host: "{{ groups.ipaserver[0] }}" -- action: member -+ name: hbacrule01 - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule sshd-pinky is present -+ # CHANGE HBACRULE WITH ALL MEMBERS -+ -+ - name: Ensure HBAC rule hbacrule01 is present with hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- hostcategory: all -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ hostgroup: testhostgroup01,testhostgroup02 -+ user: testuser01,testuser02 -+ group: testgroup01,testgroup02 -+ hbacsvc: testhbacsvc01,testhbacsvc02 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule sshd-pinky is present again -+ - name: Ensure HBAC rule hbacrule01 is present with hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- hostcategory: all -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ hostgroup: testhostgroup01,testhostgroup02 -+ user: testuser01,testuser02 -+ group: testgroup01,testgroup02 -+ hbacsvc: testhbacsvc01,testhbacsvc02 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - register: result - failed_when: result.changed - -- - name: Ensure user pinky is present in HBAC Rule sshd-pinky -+ # REMOVE MEMBERS ONE BY ONE -+ -+ - name: Ensure test HBAC rule hbacrule01 host members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- user: pinky -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ state: absent - action: member - register: result - failed_when: not result.changed - -- - name: Ensure user pinky is present in HBAC Rule sshd-pinky again -+ - name: Ensure test HBAC rule hbacrule01 host members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- user: pinky -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ state: absent - action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC service sshd is present in HBAC Rule sshd-pinky -+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- hbacsvc: sshd -+ name: hbacrule01 -+ hostgroup: testhostgroup01,testhostgroup02 -+ state: absent - action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC service sshd is present in HBAC Rule sshd-pinky again -+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- hbacsvc: sshd -+ name: hbacrule01 -+ hostgroup: testhostgroup01,testhostgroup02 -+ state: absent - action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule loginRule is present with HBAC service sshd -+ - name: Ensure test HBAC rule hbacrule01 user members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -- group: login -+ name: hbacrule01 -+ user: testuser01,testuser02 -+ state: absent -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule loginRule is present with HBAC service sshd again -+ - name: Ensure test HBAC rule hbacrule01 user members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -- group: login -+ name: hbacrule01 -+ user: testuser01,testuser02 -+ state: absent -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure user pinky is present in HBAC Rule loginRule -+ - name: Ensure test HBAC rule hbacrule01 user group members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -- user: pinky -+ name: hbacrule01 -+ group: testgroup01,testgroup02 -+ state: absent - action: member - register: result - failed_when: not result.changed - -- - name: Ensure user pinky is present in HBAC Rule loginRule again -+ - name: Ensure test HBAC rule hbacrule01 user group members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -- user: pinky -+ name: hbacrule01 -+ group: testgroup01,testgroup02 -+ state: absent - action: member - register: result - failed_when: result.changed - -- - name: Ensure user pinky is absent in HBAC Rule loginRule -+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -- user: pinky -- action: member -+ name: hbacrule01 -+ hbacsvc: testhbacsvc01,testhbacsvc02 - state: absent -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure user pinky is absent in HBAC Rule loginRule again -+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -- user: pinky -- action: member -+ name: hbacrule01 -+ hbacsvc: testhbacsvc01,testhbacsvc02 - state: absent -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule loginRule is absent -+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -+ name: hbacrule01 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - state: absent -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule loginRule is absent again -+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: loginRule -+ name: hbacrule01 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - state: absent -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC service sshd is absent in HBAC Rule sshd-pinky -+ # ADD MEMBERS BACK -+ -+ - name: Ensure test HBAC rule hbacrule01 host members are present - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- hbacsvc: sshd -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" - action: member -- state: absent - register: result - failed_when: not result.changed - -- - name: Ensure HBAC service sshd is absent in HBAC Rule sshd-pinky again -+ - name: Ensure test HBAC rule hbacrule01 host members are present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- hbacsvc: sshd -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" - action: member -- state: absent - register: result - failed_when: result.changed - -- - name: Ensure user pinky is absent in HBAC Rule sshd-pinky -+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are present - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- user: pinky -+ name: hbacrule01 -+ hostgroup: testhostgroup01,testhostgroup02 - action: member -- state: absent - register: result - failed_when: not result.changed - -- - name: Ensure user pinky is absent in HBAC Rule sshd-pinky again -+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- user: pinky -+ name: hbacrule01 -+ hostgroup: testhostgroup01,testhostgroup02 - action: member -- state: absent - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule sshd-pinky is disabled -+ - name: Ensure test HBAC rule hbacrule01 user members are present - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- state: disabled -+ name: hbacrule01 -+ user: testuser01,testuser02 -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule sshd-pinky is disabled again -+ - name: Ensure test HBAC rule hbacrule01 user members are present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- state: disabled -+ name: hbacrule01 -+ user: testuser01,testuser02 -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule sshd-pinky is enabled -+ - name: Ensure test HBAC rule hbacrule01 user group members are present - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- state: enabled -+ name: hbacrule01 -+ group: testgroup01,testgroup02 -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule sshd-pinky is enabled again -+ - name: Ensure test HBAC rule hbacrule01 user group members are present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- state: enabled -+ name: hbacrule01 -+ group: testgroup01,testgroup02 -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule sshd-pinky is absent -+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are present - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- state: absent -+ name: hbacrule01 -+ hbacsvc: testhbacsvc01,testhbacsvc02 -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule sshd-pinky is absent again -+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: sshd-pinky -- state: absent -+ name: hbacrule01 -+ hbacsvc: testhbacsvc01,testhbacsvc02 -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure host "{{ groups.ipaserver[0] }}" is absent in HBAC Rule allhosts -+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are present - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: allhosts -- host: "{{ groups.ipaserver[0] }}" -+ name: hbacrule01 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - action: member -- state: absent - register: result - failed_when: not result.changed - -- - name: Ensure host "{{ groups.ipaserver[0] }}" is absent in HBAC Rule allhosts again -+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are present again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: allhosts -- host: "{{ groups.ipaserver[0] }}" -+ name: hbacrule01 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - action: member -+ register: result -+ failed_when: result.changed -+ -+ # CHANGE TO DIFFERENT MEMBERS -+ -+ - name: Ensure HBAC rule hbacrule01 is present with different hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups -+ ipahbacrule: -+ ipaadmin_password: MyPassword123 -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost03.' + ipaserver_domain }}" -+ - "{{ 'testhost04.' + ipaserver_domain }}" -+ hostgroup: testhostgroup03,testhostgroup04 -+ user: testuser03,testuser04 -+ group: testgroup03,testgroup04 -+ hbacsvc: testhbacsvc03,testhbacsvc04 -+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure HBAC rule hbacrule01 is present with different hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups again -+ ipahbacrule: -+ ipaadmin_password: MyPassword123 -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost03.' + ipaserver_domain }}" -+ - "{{ 'testhost04.' + ipaserver_domain }}" -+ hostgroup: testhostgroup03,testhostgroup04 -+ user: testuser03,testuser04 -+ group: testgroup03,testgroup04 -+ hbacsvc: testhbacsvc03,testhbacsvc04 -+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04 -+ register: result -+ failed_when: result.changed -+ -+ # ENSURE OLD TEST MEMBERS ARE ABSENT -+ -+ - name: Ensure HBAC rule hbacrule01 members (same) are present -+ ipahbacrule: -+ ipaadmin_password: MyPassword123 -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ hostgroup: testhostgroup01,testhostgroup02 -+ user: testuser01,testuser02 -+ group: testgroup01,testgroup02 -+ hbacsvc: testhbacsvc01,testhbacsvc02 -+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02 - state: absent -+ action: member - register: result - failed_when: result.changed - -- - name: Ensure HBAC Rule allhosts is absent -+ # ENSURE NEW TEST MEMBERS ARE ABSENT -+ -+ - name: Ensure HBAC rule hbacrule01 members are absent - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: allhosts -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost03.' + ipaserver_domain }}" -+ - "{{ 'testhost04.' + ipaserver_domain }}" -+ hostgroup: testhostgroup03,testhostgroup04 -+ user: testuser03,testuser04 -+ group: testgroup03,testgroup04 -+ hbacsvc: testhbacsvc03,testhbacsvc04 -+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04 - state: absent -+ action: member - register: result - failed_when: not result.changed - -- - name: Ensure HBAC Rule allhosts is absent again -+ - name: Ensure HBAC rule hbacrule01 members are absent again - ipahbacrule: - ipaadmin_password: MyPassword123 -- name: allhosts -+ name: hbacrule01 -+ host: -+ - "{{ 'testhost03.' + ipaserver_domain }}" -+ - "{{ 'testhost04.' + ipaserver_domain }}" -+ hostgroup: testhostgroup03,testhostgroup04 -+ user: testuser03,testuser04 -+ group: testgroup03,testgroup04 -+ hbacsvc: testhbacsvc03,testhbacsvc04 -+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04 - state: absent -+ action: member - register: result - failed_when: result.changed - -- - name: User pinky absent -+ # CLEANUP TEST ITEMS -+ -+ - name: Ensure test HBAC rule hbacrule01 is absent -+ ipahbacrule: -+ ipaadmin_password: MyPassword123 -+ name: hbacrule01 -+ state: absent -+ -+ - name: Ensure test hosts are absent -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: -+ - "{{ 'testhost01.' + ipaserver_domain }}" -+ - "{{ 'testhost02.' + ipaserver_domain }}" -+ - "{{ 'testhost03.' + ipaserver_domain }}" -+ - "{{ 'testhost04.' + ipaserver_domain }}" -+ state: absent -+ -+ - name: Ensure test hostgroups are absent -+ ipahostgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhostgroup01,testhostgroup02,testhostgroup03,testhostgroup04 -+ state: absent -+ -+ - name: Ensure test users are absent - ipauser: - ipaadmin_password: MyPassword123 -- name: pinky -+ name: testuser01,testuser02,testuser03,testuser04 - state: absent - -- - name: User group login absent -+ - name: Ensure test user groups are absent - ipagroup: - ipaadmin_password: MyPassword123 -- name: login -+ name: testgroup01,testgroup02,testgroup03,testgroup04 -+ state: absent -+ -+ - name: Ensure test HBAC Services are absent -+ ipahbacsvc: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvc01,testhbacsvc02,testhbacsvc03,testhbacsvc04 -+ state: absent -+ -+ - name: Ensure test HBAC Service Groups are absent -+ ipahbacsvcgroup: -+ ipaadmin_password: MyPassword123 -+ name: testhbacsvcgroup01,testhbacsvcgroup02,testhbacsvcgroup03,testhbacsvcgroup04 - state: absent diff --git a/SOURCES/ansible-freeipa-0.1.8-ipahost-Do-not-fail-on-missing-DNS-or-zone-when-no-IP-address-given_rhbz#1804838.patch b/SOURCES/ansible-freeipa-0.1.8-ipahost-Do-not-fail-on-missing-DNS-or-zone-when-no-IP-address-given_rhbz#1804838.patch deleted file mode 100644 index 512a213..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipahost-Do-not-fail-on-missing-DNS-or-zone-when-no-IP-address-given_rhbz#1804838.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 22d8784da29dcfede0744ef6b691b4506eae5deb Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Thu, 20 Feb 2020 12:58:11 +0100 -Subject: [PATCH] ipahost: Do not fail on missing DNS or zone when no IP - address given - -If no IP address is given and either DNS is not configured or if the zone is -not found then ipahost may not fail in dnsrecord_find. - -The error happened for example by ensuring the absence of a host that is not -part of the domain or for a host that has been added with force and is using -a domain that is not served by the DNS server in the domain. It also -happened if there was no DNS server in the domain at all. - -A new test case has been added to test_host_ipaddresses.yml - -The fix requires ipalib_errors provided by ansible_freeipa_module. - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1804838 ---- - plugins/modules/ipahost.py | 17 +++++++++++++++-- - tests/host/test_host_ipaddresses.yml | 9 +++++++++ - 2 files changed, 24 insertions(+), 2 deletions(-) - -diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py -index 558560e..062f768 100644 ---- a/plugins/modules/ipahost.py -+++ b/plugins/modules/ipahost.py -@@ -409,7 +409,7 @@ - from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \ -- is_ipv4_addr, is_ipv6_addr -+ is_ipv4_addr, is_ipv6_addr, ipalib_errors - import six - - -@@ -871,7 +871,20 @@ def main(): - - # Make sure host exists - res_find = find_host(ansible_module, name) -- res_find_dnsrecord = find_dnsrecord(ansible_module, name) -+ try: -+ res_find_dnsrecord = find_dnsrecord(ansible_module, name) -+ except ipalib_errors.NotFound as e: -+ msg = str(e) -+ if ip_address is None and \ -+ ("DNS is not configured" in msg or \ -+ "DNS zone not found" in msg): -+ # IP address(es) not given and no DNS support in IPA -+ # -> Ignore failure -+ # IP address(es) not given and DNS zone is not found -+ # -> Ignore failure -+ res_find_dnsrecord = None -+ else: -+ ansible_module.fail_json(msg="%s: %s" % (host, msg)) - - # Create command - if state == "present": -diff --git a/tests/host/test_host_ipaddresses.yml b/tests/host/test_host_ipaddresses.yml -index 0a97dd5..136a610 100644 ---- a/tests/host/test_host_ipaddresses.yml -+++ b/tests/host/test_host_ipaddresses.yml -@@ -301,6 +301,15 @@ - register: result - failed_when: result.changed - -+ - name: Absent host01.ihavenodns.info test -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: host01.ihavenodns.info -+ state: absent -+ register: result -+ failed_when: result.changed -+ - - name: Host absent - ipahost: - ipaadmin_password: MyPassword123 -From 4d94cb09a9fb09dd2576223b9be7f77d515202fb Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Thu, 20 Feb 2020 12:54:32 +0100 -Subject: [PATCH] ansible_freeipa_module: Import ipalib.errors as ipalib_errors - -For beeing able to catch ipalib.errors.NotFound errors in ipahost it is -needed to import ipalib.errors. ipalib.errors is now imported as -ipalib_errors to not have name conflicts with the errors list used in some -of the modules. - -Related: https://bugzilla.redhat.com/show_bug.cgi?id=1804838 ---- - plugins/module_utils/ansible_freeipa_module.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py -index 6acdbef..5066de3 100644 ---- a/plugins/module_utils/ansible_freeipa_module.py -+++ b/plugins/module_utils/ansible_freeipa_module.py -@@ -28,6 +28,7 @@ - import gssapi - from datetime import datetime - from ipalib import api -+from ipalib import errors as ipalib_errors - from ipalib.config import Env - from ipalib.constants import DEFAULT_CONFIG, LDAP_GENERALIZED_TIME_FORMAT - try: diff --git a/SOURCES/ansible-freeipa-0.1.8-ipahost-Enhanced-failure-msg-for-member-params-used-without-member-action_rhbz#1783948.patch b/SOURCES/ansible-freeipa-0.1.8-ipahost-Enhanced-failure-msg-for-member-params-used-without-member-action_rhbz#1783948.patch deleted file mode 100644 index aea3a62..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipahost-Enhanced-failure-msg-for-member-params-used-without-member-action_rhbz#1783948.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 24515e40ad289552d45bddd33c7a0dda93117a7f Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Wed, 18 Dec 2019 12:28:03 +0100 -Subject: [PATCH] ipahost: Enhanced failure msg for member params used without - member action - -The failure message if member parameters like certificate, managedby_host, -principal, allow_create_keytab_* and allow_retrieve_keytab_* are used -without member action for state absent has been enhanced to propose the -member action. ---- - plugins/modules/ipahost.py | 20 +++++++++++++------- - 1 file changed, 13 insertions(+), 7 deletions(-) - -diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py -index ec5e196..8ee9532 100644 ---- a/plugins/modules/ipahost.py -+++ b/plugins/modules/ipahost.py -@@ -511,19 +511,25 @@ def check_parameters( - "userclass", "auth_ind", "requires_pre_auth", - "ok_as_delegate", "ok_to_auth_as_delegate", "force", - "reverse", "ip_address", "update_password"] -+ for x in invalid: -+ if vars()[x] is not None: -+ module.fail_json( -+ msg="Argument '%s' can not be used with state '%s'" % -+ (x, state)) - if action == "host": -- invalid.extend([ -+ invalid = [ - "certificate", "managedby_host", "principal", - "allow_create_keytab_user", "allow_create_keytab_group", - "allow_create_keytab_host", "allow_create_keytab_hostgroup", - "allow_retrieve_keytab_user", "allow_retrieve_keytab_group", - "allow_retrieve_keytab_host", -- "allow_retrieve_keytab_hostgroup"]) -- for x in invalid: -- if vars()[x] is not None: -- module.fail_json( -- msg="Argument '%s' can not be used with state '%s'" % -- (x, state)) -+ "allow_retrieve_keytab_hostgroup" -+ ] -+ for x in invalid: -+ if vars()[x] is not None: -+ module.fail_json( -+ msg="Argument '%s' can only be used with action " -+ "'member' for state '%s'" % (x, state)) - - - def main(): diff --git a/SOURCES/ansible-freeipa-0.1.8-ipahost-Fail-on-action-member-for-new-hosts-fix-dnsrecord_add-reverse-flag_rhbz#1803026.patch b/SOURCES/ansible-freeipa-0.1.8-ipahost-Fail-on-action-member-for-new-hosts-fix-dnsrecord_add-reverse-flag_rhbz#1803026.patch deleted file mode 100644 index fa2dadd..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipahost-Fail-on-action-member-for-new-hosts-fix-dnsrecord_add-reverse-flag_rhbz#1803026.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0816b0773b1535780c7c3e5f05bda39434ab6bac Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Fri, 14 Feb 2020 13:21:54 +0100 -Subject: [PATCH] ipahost: Fail on action member for new hosts, fix - dnsrecord_add reverse flag - -The check to make sure that member can not be used on non existing hosts -has bee missing. Also the reverse flag for the dnsrecord_add call was None -if the varaible was not set. - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1803026 ---- - plugins/modules/ipahost.py | 23 +++++++++++++++-------- - 1 file changed, 15 insertions(+), 8 deletions(-) - -diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py -index a5fd482..558560e 100644 ---- a/plugins/modules/ipahost.py -+++ b/plugins/modules/ipahost.py -@@ -1005,6 +1005,11 @@ def main(): - dnsrecord_args.get("aaaarecord"), - _dnsrec.get("aaaarecord")) - -+ else: -+ if res_find is None: -+ ansible_module.fail_json( -+ msg="No host '%s'" % name) -+ - if action != "host" or (action == "host" and res_find is None): - certificate_add = certificate or [] - certificate_del = [] -@@ -1178,15 +1183,17 @@ def main(): - domain_name = name[name.find(".")+1:] - host_name = name[:name.find(".")] - -+ _args = {"idnsname": host_name} -+ if reverse is not None: -+ _args["a_extra_create_reverse"] = reverse -+ _args["aaaa_extra_create_reverse"] = reverse -+ if len(dnsrecord_a_add) > 0: -+ _args["arecord"] = dnsrecord_a_add -+ if len(dnsrecord_aaaa_add) > 0: -+ _args["aaaarecord"] = dnsrecord_aaaa_add -+ - commands.append([domain_name, -- "dnsrecord_add", -- { -- "idnsname": host_name, -- "arecord": dnsrecord_a_add, -- "a_extra_create_reverse": reverse, -- "aaaarecord": dnsrecord_aaaa_add, -- "aaaa_extra_create_reverse": reverse -- }]) -+ "dnsrecord_add", _args]) - - if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0: - domain_name = name[name.find(".")+1:] diff --git a/SOURCES/ansible-freeipa-0.1.8-ipahost-Fix-choices-of-auth_ind-parameter-allow-to-reset-parameter_rhbz#1783992.patch b/SOURCES/ansible-freeipa-0.1.8-ipahost-Fix-choices-of-auth_ind-parameter-allow-to-reset-parameter_rhbz#1783992.patch deleted file mode 100644 index 592da6e..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipahost-Fix-choices-of-auth_ind-parameter-allow-to-reset-parameter_rhbz#1783992.patch +++ /dev/null @@ -1,78 +0,0 @@ -From b6100f0c19e2caf73ab70bbc572d3e47e6066b48 Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Tue, 17 Dec 2019 14:04:43 +0100 -Subject: [PATCH] ipahost: Fix choices of auth_ind parameter, allow to reset - parameter - -The choices for the auth_ind parameter have been wrong. The choices are now -['radius', 'otp', 'pkinit', 'hardened', '']. The empty string has been added -to be able to rest auth_ind for the host entry. ---- - README-host.md | 2 +- - plugins/modules/ipahost.py | 15 ++++++++++++--- - 2 files changed, 13 insertions(+), 4 deletions(-) - -diff --git a/README-host.md b/README-host.md -index edec8d9..be5ad79 100644 ---- a/README-host.md -+++ b/README-host.md -@@ -280,7 +280,7 @@ Variable | Description | Required - `mac_address` \| `macaddress` | List of hardware MAC addresses. | no - `sshpubkey` \| `ipasshpubkey` | List of SSH public keys | no - `userclass` \| `class` | Host category (semantics placed on this attribute are for local interpretation) | no --`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened"] | no -+`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened", ""] | no - `requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service (bool) | no - `ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service (bool) | no - `ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no -diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py -index ec5e196..b130395 100644 ---- a/plugins/modules/ipahost.py -+++ b/plugins/modules/ipahost.py -@@ -147,9 +147,10 @@ - Defines a whitelist for Authentication Indicators. Use 'otp' to allow - OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA - authentications. Other values may be used for custom configurations. -+ Use empty string to reset auth_ind to the initial value. - type: list - aliases: ["krbprincipalauthind"] -- choices: ["radius", "otp", "pkinit", "hardened"] -+ choices: ["radius", "otp", "pkinit", "hardened", ""] - required: false - requires_pre_auth: - description: Pre-authentication is required for the service -@@ -277,9 +278,10 @@ - Defines a whitelist for Authentication Indicators. Use 'otp' to allow - OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA - authentications. Other values may be used for custom configurations. -+ Use empty string to reset auth_ind to the initial value. - type: list - aliases: ["krbprincipalauthind"] -- choices: ["radius", "otp", "pkinit", "hardened"] -+ choices: ["radius", "otp", "pkinit", "hardened", ""] - required: false - requires_pre_auth: - description: Pre-authentication is required for the service -@@ -590,7 +592,7 @@ def main(): - default=None), - auth_ind=dict(type='list', aliases=["krbprincipalauthind"], - default=None, -- choices=['password', 'radius', 'otp']), -+ choices=['radius', 'otp', 'pkinit', 'hardened', '']), - requires_pre_auth=dict(type="bool", aliases=["ipakrbrequirespreauth"], - default=None), - ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"], -@@ -835,6 +837,13 @@ def main(): - if x in args: - del args[x] - -+ # Ignore auth_ind if it is empty (for resetting) -+ # and not set in for the host -+ if "krbprincipalauthind" not in res_find and \ -+ "krbprincipalauthind" in args and \ -+ args["krbprincipalauthind"] == ['']: -+ del args["krbprincipalauthind"] -+ - # For all settings is args, check if there are - # different settings in the find result. - # If yes: modify diff --git a/SOURCES/ansible-freeipa-0.1.8-ipapwpolicy-Use-global_policy-if-name-is-not-set_rhbz#1797532.patch b/SOURCES/ansible-freeipa-0.1.8-ipapwpolicy-Use-global_policy-if-name-is-not-set_rhbz#1797532.patch deleted file mode 100644 index bde4624..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipapwpolicy-Use-global_policy-if-name-is-not-set_rhbz#1797532.patch +++ /dev/null @@ -1,179 +0,0 @@ -From 4dd1d25eacd1481be0a881a017144ff4d3396ccd Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Thu, 6 Feb 2020 15:38:00 +0100 -Subject: [PATCH] ipapwpolicy: Use global_policy if name is not set - -If the name is not set, the policy global_policy is now used. It was needed -before to explicitly name the global_policy. Also a check has been added -to fail early if global_policy is used with state absent. - -The README for pwpolicy has been extended with an example for global_policy -and also the description of the name variable. - -The test has also been extended to check a change of maxlife for -global_policy and that global_policy can not be used with state: absent - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1797532 ---- - README-pwpolicy.md | 19 +++++++++++-- - plugins/modules/ipapwpolicy.py | 9 ++++-- - tests/pwpolicy/test_pwpolicy.yml | 49 ++++++++++++++++++++++++++++++++ - 3 files changed, 73 insertions(+), 4 deletions(-) - -diff --git a/README-pwpolicy.md b/README-pwpolicy.md -index 16306b7..847b32d 100644 ---- a/README-pwpolicy.md -+++ b/README-pwpolicy.md -@@ -56,7 +56,7 @@ Example playbook to ensure presence of pwpolicies for exisiting group ops: - maxfail: 3 - ``` - --Example playbook to ensure absence of pwpolicies for group ops -+Example playbook to ensure absence of pwpolicies for group ops: - - ```yaml - --- -@@ -72,6 +72,21 @@ Example playbook to ensure absence of pwpolicies for group ops - state: absent - ``` - -+Example playbook to ensure maxlife is set to 49 in global policy: -+ -+```yaml -+--- -+- name: Playbook to handle pwpolicies -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ # Ensure absence of pwpolicies for group ops -+ - ipapwpolicy: -+ ipaadmin_password: MyPassword123 -+ maxlife: 49 -+``` -+ - - Variables - ========= -@@ -83,7 +98,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 --`name` \| `cn` | The list of pwpolicy name strings. | no -+`name` \| `cn` | The list of pwpolicy name strings. If name is not given, `global_policy` will be used automatically. | no - `maxlife` \| `krbmaxpwdlife` | Maximum password lifetime in days. (int) | no - `minlife` \| `krbminpwdlife` | Minimum password lifetime in hours. (int) | no - `history` \| `krbpwdhistorylength` | Password history size. (int) | no -diff --git a/plugins/modules/ipapwpolicy.py b/plugins/modules/ipapwpolicy.py -index 9437b59..f168703 100644 ---- a/plugins/modules/ipapwpolicy.py -+++ b/plugins/modules/ipapwpolicy.py -@@ -167,7 +167,7 @@ def main(): - ipaadmin_password=dict(type="str", required=False, no_log=True), - - name=dict(type="list", aliases=["cn"], default=None, -- required=True), -+ required=False), - # present - - maxlife=dict(type="int", aliases=["krbmaxpwdlife"], default=None), -@@ -218,6 +218,9 @@ def main(): - - # Check parameters - -+ if names is None: -+ names = ["global_policy"] -+ - if state == "present": - if len(names) != 1: - ansible_module.fail_json( -@@ -225,8 +228,10 @@ def main(): - - if state == "absent": - if len(names) < 1: -+ ansible_module.fail_json(msg="No name given.") -+ if "global_policy" in names: - ansible_module.fail_json( -- msg="No name given.") -+ msg="'global_policy' can not be made absent.") - invalid = ["maxlife", "minlife", "history", "minclasses", - "minlength", "priority", "maxfail", "failinterval", - "lockouttime"] -diff --git a/tests/pwpolicy/test_pwpolicy.yml b/tests/pwpolicy/test_pwpolicy.yml -index 5c69345..f93f275 100644 ---- a/tests/pwpolicy/test_pwpolicy.yml -+++ b/tests/pwpolicy/test_pwpolicy.yml -@@ -5,10 +5,30 @@ - gather_facts: false - - tasks: -+ - name: Ensure maxlife of 90 for global_policy -+ ipapwpolicy: -+ ipaadmin_password: SomeADMINpassword -+ maxlife: 90 -+ -+ - name: Ensure absence of group ops -+ ipagroup: -+ ipaadmin_password: SomeADMINpassword -+ name: ops -+ state: absent -+ -+ - name: Ensure absence of pwpolicies for group ops -+ ipapwpolicy: -+ ipaadmin_password: SomeADMINpassword -+ name: ops -+ state: absent -+ - - name: Ensure presence of group ops - ipagroup: - ipaadmin_password: SomeADMINpassword - name: ops -+ state: present -+ register: result -+ failed_when: not result.changed - - - name: Ensure presence of pwpolicies for group ops - ipapwpolicy: -@@ -42,6 +62,28 @@ - register: result - failed_when: result.changed - -+ - name: Ensure maxlife of 49 for global_policy -+ ipapwpolicy: -+ ipaadmin_password: SomeADMINpassword -+ maxlife: 49 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Ensure maxlife of 49 for global_policy again -+ ipapwpolicy: -+ ipaadmin_password: SomeADMINpassword -+ maxlife: 49 -+ register: result -+ failed_when: result.changed -+ -+ - name: Ensure absence of pwpoliciy global_policy will fail -+ ipapwpolicy: -+ ipaadmin_password: SomeADMINpassword -+ state: absent -+ register: result -+ ignore_errors: True -+ failed_when: result is defined and result -+ - - name: Ensure absence of pwpolicies for group ops - ipapwpolicy: - ipaadmin_password: SomeADMINpassword -@@ -50,6 +92,13 @@ - register: result - failed_when: not result.changed - -+ - name: Ensure maxlife of 90 for global_policy -+ ipapwpolicy: -+ ipaadmin_password: MyPassword123 -+ maxlife: 90 -+ register: result -+ failed_when: not result.changed -+ - - name: Ensure absence of pwpolicies for group ops - ipapwpolicy: - ipaadmin_password: SomeADMINpassword diff --git a/SOURCES/ansible-freeipa-0.1.8-ipauser-Allow-reset-of-userauthtype-do-not-depend-on-first-last-for-mod_rhbz#1784474.patch b/SOURCES/ansible-freeipa-0.1.8-ipauser-Allow-reset-of-userauthtype-do-not-depend-on-first-last-for-mod_rhbz#1784474.patch deleted file mode 100644 index cb0a7ef..0000000 --- a/SOURCES/ansible-freeipa-0.1.8-ipauser-Allow-reset-of-userauthtype-do-not-depend-on-first-last-for-mod_rhbz#1784474.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 36c1c837086c42049f09cf689a1ebd61627abae0 Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Tue, 17 Dec 2019 15:30:45 +0100 -Subject: [PATCH] ipauser: Allow reset of userauthtype, do not depend on - first,last for mod - -It was not possible to reset the userauthtype. The empty string has been -added to userauthtype for this. - -Also ipauser will only depend on given first and last name if the user -does not exist yet. For the update operation these parameters are not -needed anymore. ---- - README-user.md | 2 +- - plugins/modules/ipauser.py | 38 ++++++++++++++++++++++++++------------ - 2 files changed, 27 insertions(+), 13 deletions(-) - -diff --git a/README-user.md b/README-user.md -index 56772a7..991121c 100644 ---- a/README-user.md -+++ b/README-user.md -@@ -408,7 +408,7 @@ Variable | Description | Required - `manager` | List of manager user names. | no - `carlicense` | List of car licenses. | no - `sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no --`userauthtype` | List of supported user authentication types. Choices: `password`, `radius` and `otp` | no -+`userauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp` and ``. Use empty string to reset userauthtype to the initial value. | no - `userclass` | User category. (semantics placed on this attribute are for local interpretation). | no - `radius` | RADIUS proxy configuration | no - `radiususer` | RADIUS proxy username | no -diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py -index ac45295..36e8bae 100644 ---- a/plugins/modules/ipauser.py -+++ b/plugins/modules/ipauser.py -@@ -153,9 +153,12 @@ - required: false - aliases: ["ipasshpubkey"] - userauthtype: -- description: List of supported user authentication types -- choices=['password', 'radius', 'otp'] -+ description: -+ List of supported user authentication types -+ Use empty string to reset userauthtype to the initial value. -+ choices=['password', 'radius', 'otp', ''] - required: false -+ aliases: ["ipauserauthtype"] - userclass: - description: - - User category -@@ -310,9 +313,12 @@ - required: false - aliases: ["ipasshpubkey"] - userauthtype: -- description: List of supported user authentication types -- choices=['password', 'radius', 'otp'] -+ description: -+ List of supported user authentication types -+ Use empty string to reset userauthtype to the initial value. -+ choices=['password', 'radius', 'otp', ''] - required: false -+ aliases: ["ipauserauthtype"] - userclass: - description: - - User category -@@ -701,7 +707,7 @@ def main(): - default=None), - userauthtype=dict(type='list', aliases=["ipauserauthtype"], - default=None, -- choices=['password', 'radius', 'otp']), -+ choices=['password', 'radius', 'otp', '']), - userclass=dict(type="list", aliases=["class"], - default=None), - radius=dict(type="str", aliases=["ipatokenradiusconfiglink"], -@@ -845,13 +851,6 @@ def main(): - if names is not None and len(names) != 1: - ansible_module.fail_json( - msg="Only one user can be added at a time using name.") -- if action != "member": -- # Only check first and last here if names is set -- if names is not None: -- if first is None: -- ansible_module.fail_json(msg="First name is needed") -- if last is None: -- ansible_module.fail_json(msg="Last name is needed") - - check_parameters( - ansible_module, state, action, -@@ -1011,6 +1010,13 @@ def main(): - if "noprivate" in args: - del args["noprivate"] - -+ # Ignore userauthtype if it is empty (for resetting) -+ # and not set in for the user -+ if "ipauserauthtype" not in res_find and \ -+ "ipauserauthtype" in args and \ -+ args["ipauserauthtype"] == ['']: -+ del args["ipauserauthtype"] -+ - # For all settings is args, check if there are - # different settings in the find result. - # If yes: modify -@@ -1019,6 +1025,14 @@ def main(): - commands.append([name, "user_mod", args]) - - else: -+ # Make sure we have a first and last name -+ if first is None: -+ ansible_module.fail_json( -+ msg="First name is needed") -+ if last is None: -+ ansible_module.fail_json( -+ msg="Last name is needed") -+ - commands.append([name, "user_add", args]) - - # Handle members: principal, manager, certificate and diff --git a/SOURCES/ansible-freeipa-ipahost-Add-support-for-several-IP-addresses-and-also-to-change-them_rhbz#1783979,1783976.patch b/SOURCES/ansible-freeipa-ipahost-Add-support-for-several-IP-addresses-and-also-to-change-them_rhbz#1783979,1783976.patch deleted file mode 100644 index 4f2093d..0000000 --- a/SOURCES/ansible-freeipa-ipahost-Add-support-for-several-IP-addresses-and-also-to-change-them_rhbz#1783979,1783976.patch +++ /dev/null @@ -1,915 +0,0 @@ -From 167c76311da72c2bfabf4b2bce9e128c11d519d0 Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Wed, 12 Feb 2020 16:54:13 +0100 -Subject: [PATCH] ipahost: Add support for several IP addresses and also to - change them - -ipahost was so far ignoring IP addresses when the host already existed. -This happened because host_mod is not providing functionality to do this. -Now ipaddress is a list and it is possible to ensure a host with several -IP addresses (these can be IPv4 and IPv6). Also it is possible to ensure -presence and absence of IP addresses for an exising host using action -member. - -There are no IP address conclict checks as this would lead into issues with -updating an existing host that already is using a duplicate IP address for -example for round-robin (RR). Also this might lead into issues with ensuring -a new host with several IP addresses in this case. Also to ensure a list of -hosts with changing the IP address of one host to another in the list would -result in issues here. - -New example playbooks have been added: - - playbooks/host/host-present-with-several-ip-addresses.yml - playbooks/host/host-member-ipaddresses-absent.yml - playbooks/host/host-member-ipaddresses-present.yml - -A new test has been added for verification: - - tests/host/test_host_ipaddresses.yml - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1783976 - https://bugzilla.redhat.com/show_bug.cgi?id=1783979 ---- - README-host.md | 79 ++++- - .../host/host-member-ipaddresses-absent.yml | 17 + - .../host/host-member-ipaddresses-present.yml | 16 + - ...host-present-with-several-ip-addresses.yml | 24 ++ - .../module_utils/ansible_freeipa_module.py | 23 ++ - plugins/modules/ipahost.py | 179 +++++++--- - tests/host/test_host_ipaddresses.yml | 312 ++++++++++++++++++ - 7 files changed, 600 insertions(+), 50 deletions(-) - create mode 100644 playbooks/host/host-member-ipaddresses-absent.yml - create mode 100644 playbooks/host/host-member-ipaddresses-present.yml - create mode 100644 playbooks/host/host-present-with-several-ip-addresses.yml - create mode 100644 tests/host/test_host_ipaddresses.yml - -diff --git a/README-host.md b/README-host.md -index be5ad79..ecc59a9 100644 ---- a/README-host.md -+++ b/README-host.md -@@ -65,6 +65,79 @@ Example playbook to ensure host presence: - - "52:54:00:BD:97:1E" - state: present - ``` -+Compared to `ipa host-add` command no IP address conflict check is done as the ipahost module supports to have several IPv4 and IPv6 addresses for a host. -+ -+ -+Example playbook to ensure host presence with several IP addresses: -+ -+```yaml -+--- -+- name: Playbook to handle hosts -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ # Ensure host is present -+ - ipahost: -+ ipaadmin_password: MyPassword123 -+ name: host01.example.com -+ description: Example host -+ ip_address: -+ - 192.168.0.123 -+ - 192.168.0.124 -+ - fe80::20c:29ff:fe02:a1b3 -+ - fe80::20c:29ff:fe02:a1b4 -+ locality: Lab -+ ns_host_location: Lab -+ ns_os_version: CentOS 7 -+ ns_hardware_platform: Lenovo T61 -+ mac_address: -+ - "08:00:27:E3:B1:2D" -+ - "52:54:00:BD:97:1E" -+ state: present -+``` -+ -+ -+Example playbook to ensure IP addresses are present for a host: -+ -+```yaml -+--- -+- name: Playbook to handle hosts -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ # Ensure host is present -+ - ipahost: -+ ipaadmin_password: MyPassword123 -+ name: host01.example.com -+ ip_address: -+ - 192.168.0.124 -+ - fe80::20c:29ff:fe02:a1b4 -+ action: member -+ state: present -+``` -+ -+ -+Example playbook to ensure IP addresses are absent for a host: -+ -+```yaml -+--- -+- name: Playbook to handle hosts -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ # Ensure host is present -+ - ipahost: -+ ipaadmin_password: MyPassword123 -+ name: host01.example.com -+ ip_address: -+ - 192.168.0.124 -+ - fe80::20c:29ff:fe02:a1b4 -+ action: member -+ state: absent -+``` - - - Example playbook to ensure host presence without DNS: -@@ -215,7 +288,7 @@ Example playbook to disable a host: - update_dns: yes - state: disabled - ``` --`update_dns` controls if the DNS entries will be updated. -+`update_dns` controls if the DNS entries will be updated in this case. For `state` present it is controlling the update of the DNS SSHFP records, but not the the other DNS records. - - - Example playbook to ensure a host is absent: -@@ -286,8 +359,8 @@ Variable | Description | Required - `ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no - `force` | Force host name even if not in DNS. | no - `reverse` | Reverse DNS detection. | no --`ip_address` \| `ipaddress` | The host IP address. | no --`update_dns` | Update DNS entries. | no -+`ip_address` \| `ipaddress` | The host IP address list. It can contain IPv4 and IPv6 addresses. No conflict check for IP addresses is done. | no -+`update_dns` | For existing hosts: DNS SSHFP records are updated with `state` present and all DNS entries for a host removed with `state` absent. | no - - - Return Values -diff --git a/playbooks/host/host-member-ipaddresses-absent.yml b/playbooks/host/host-member-ipaddresses-absent.yml -new file mode 100644 -index 0000000..2466dbd ---- /dev/null -+++ b/playbooks/host/host-member-ipaddresses-absent.yml -@@ -0,0 +1,17 @@ -+--- -+- name: Host member IP addresses absent -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ - name: Ensure host01.example.com IP addresses absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: host01.example.com -+ ip_address: -+ - 192.168.0.123 -+ - fe80::20c:29ff:fe02:a1b3 -+ - 192.168.0.124 -+ - fe80::20c:29ff:fe02:a1b4 -+ action: member -+ state: absent -diff --git a/playbooks/host/host-member-ipaddresses-present.yml b/playbooks/host/host-member-ipaddresses-present.yml -new file mode 100644 -index 0000000..f473993 ---- /dev/null -+++ b/playbooks/host/host-member-ipaddresses-present.yml -@@ -0,0 +1,16 @@ -+--- -+- name: Host member IP addresses present -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ - name: Ensure host01.example.com IP addresses present -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: host01.example.com -+ ip_address: -+ - 192.168.0.123 -+ - fe80::20c:29ff:fe02:a1b3 -+ - 192.168.0.124 -+ - fe80::20c:29ff:fe02:a1b4 -+ action: member -diff --git a/playbooks/host/host-present-with-several-ip-addresses.yml b/playbooks/host/host-present-with-several-ip-addresses.yml -new file mode 100644 -index 0000000..4956562 ---- /dev/null -+++ b/playbooks/host/host-present-with-several-ip-addresses.yml -@@ -0,0 +1,24 @@ -+--- -+- name: Host present with several IP addresses -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ - name: Ensure host is present -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: host01.example.com -+ description: Example host -+ ip_address: -+ - 192.168.0.123 -+ - fe80::20c:29ff:fe02:a1b3 -+ - 192.168.0.124 -+ - fe80::20c:29ff:fe02:a1b4 -+ locality: Lab -+ ns_host_location: Lab -+ ns_os_version: CentOS 7 -+ ns_hardware_platform: Lenovo T61 -+ mac_address: -+ - "08:00:27:E3:B1:2D" -+ - "52:54:00:BD:97:1E" -+ state: present -diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py -index 9e97b88..6acdbef 100644 ---- a/plugins/module_utils/ansible_freeipa_module.py -+++ b/plugins/module_utils/ansible_freeipa_module.py -@@ -42,6 +42,7 @@ - from ipalib.x509 import Encoding - except ImportError: - from cryptography.hazmat.primitives.serialization import Encoding -+import socket - import base64 - import six - -@@ -285,3 +286,25 @@ def encode_certificate(cert): - if not six.PY2: - encoded = encoded.decode('ascii') - return encoded -+ -+ -+def is_ipv4_addr(ipaddr): -+ """ -+ Test if figen IP address is a valid IPv4 address -+ """ -+ try: -+ socket.inet_pton(socket.AF_INET, ipaddr) -+ except socket.error: -+ return False -+ return True -+ -+ -+def is_ipv6_addr(ipaddr): -+ """ -+ Test if figen IP address is a valid IPv6 address -+ """ -+ try: -+ socket.inet_pton(socket.AF_INET6, ipaddr) -+ except socket.error: -+ return False -+ return True -diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py -index dba4181..a5fd482 100644 ---- a/plugins/modules/ipahost.py -+++ b/plugins/modules/ipahost.py -@@ -176,11 +176,16 @@ - default: true - required: false - ip_address: -- description: The host IP address -+ description: -+ The host IP address list (IPv4 and IPv6). No IP address conflict -+ check will be done. - aliases: ["ipaddress"] - required: false - update_dns: -- description: Update DNS entries -+ description: -+ Controls the update of the DNS SSHFP records for existing hosts and -+ the removal of all DNS entries if a host gets removed with state -+ absent. - required: false - description: - description: The host description -@@ -306,11 +311,16 @@ - default: true - required: false - ip_address: -- description: The host IP address -+ description: -+ The host IP address list (IPv4 and IPv6). No IP address conflict -+ check will be done. - aliases: ["ipaddress"] - required: false - update_dns: -- description: Update DNS entries -+ description: -+ Controls the update of the DNS SSHFP records for existing hosts and -+ the removal of all DNS entries if a host gets removed with state -+ absent. - required: false - update_password: - description: -@@ -398,7 +408,8 @@ - from ansible.module_utils._text import to_text - from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ -- module_params_get, gen_add_del_lists, encode_certificate, api_get_realm -+ module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \ -+ is_ipv4_addr, is_ipv6_addr - import six - - -@@ -428,6 +439,32 @@ def find_host(module, name): - return None - - -+def find_dnsrecord(module, name): -+ domain_name = name[name.find(".")+1:] -+ host_name = name[:name.find(".")] -+ -+ _args = { -+ "all": True, -+ "idnsname": to_text(host_name), -+ } -+ -+ _result = api_command(module, "dnsrecord_find", to_text(domain_name), -+ _args) -+ -+ if len(_result["result"]) > 1: -+ module.fail_json( -+ msg="There is more than one host '%s'" % (name)) -+ elif len(_result["result"]) == 1: -+ _res = _result["result"][0] -+ certs = _res.get("usercertificate") -+ if certs is not None: -+ _res["usercertificate"] = [encode_certificate(cert) for -+ cert in certs] -+ return _res -+ else: -+ return None -+ -+ - def show_host(module, name): - _result = api_command(module, "host_show", to_text(name), {}) - return _result["result"] -@@ -470,16 +507,34 @@ def gen_args(description, locality, location, platform, os, password, random, - _args["ipakrboktoauthasdelegate"] = ok_to_auth_as_delegate - if force is not None: - _args["force"] = force -- if reverse is not None: -- _args["no_reverse"] = not reverse - if ip_address is not None: -- _args["ip_address"] = ip_address -+ # IP addresses are handed extra, therefore it is needed to set -+ # the force option here to make sure that host-add is able to -+ # add a host without IP address. -+ _args["force"] = True - if update_dns is not None: - _args["updatedns"] = update_dns - - return _args - - -+def gen_dnsrecord_args(module, ip_address, reverse): -+ _args = {} -+ if reverse is not None: -+ _args["a_extra_create_reverse"] = reverse -+ _args["aaaa_extra_create_reverse"] = reverse -+ if ip_address is not None: -+ for ip in ip_address: -+ if is_ipv4_addr(ip): -+ _args.setdefault("arecord", []).append(ip) -+ elif is_ipv6_addr(ip): -+ _args.setdefault("aaaarecord", []).append(ip) -+ else: -+ module.fail_json(msg="'%s' is not a valid IP address." % ip) -+ -+ return _args -+ -+ - def check_parameters( - module, state, action, - description, locality, location, platform, os, password, random, -@@ -499,8 +554,7 @@ def check_parameters( - "os", "password", "random", "mac_address", "sshpubkey", - "userclass", "auth_ind", "requires_pre_auth", - "ok_as_delegate", "ok_to_auth_as_delegate", "force", -- "reverse", "ip_address", "update_dns", -- "update_password"] -+ "reverse", "update_dns", "update_password"] - for x in invalid: - if vars()[x] is not None: - module.fail_json( -@@ -512,7 +566,7 @@ def check_parameters( - "password", "random", "mac_address", "sshpubkey", - "userclass", "auth_ind", "requires_pre_auth", - "ok_as_delegate", "ok_to_auth_as_delegate", "force", -- "reverse", "ip_address", "update_password"] -+ "reverse", "update_password"] - for x in invalid: - if vars()[x] is not None: - module.fail_json( -@@ -549,9 +603,6 @@ def main(): - default=None, no_log=True), - random=dict(type="bool", aliases=["random_password"], - default=None), -- -- -- - certificate=dict(type="list", aliases=["usercertificate"], - default=None), - managedby_host=dict(type="list", -@@ -608,7 +659,7 @@ def main(): - default=None), - force=dict(type='bool', default=None), - reverse=dict(type='bool', default=None), -- ip_address=dict(type="str", aliases=["ipaddress"], -+ ip_address=dict(type="list", aliases=["ipaddress"], - default=None), - update_dns=dict(type="bool", aliases=["updatedns"], - default=None), -@@ -820,6 +871,7 @@ def main(): - - # Make sure host exists - res_find = find_host(ansible_module, name) -+ res_find_dnsrecord = find_dnsrecord(ansible_module, name) - - # Create command - if state == "present": -@@ -829,6 +881,8 @@ def main(): - random, mac_address, sshpubkey, userclass, auth_ind, - requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate, - force, reverse, ip_address, update_dns) -+ dnsrecord_args = gen_dnsrecord_args( -+ ansible_module, ip_address, reverse) - - if action == "host": - # Found the host -@@ -938,39 +992,20 @@ def main(): - res_find.get( - "ipaallowedtoperform_read_keys_hostgroup")) - -- else: -- certificate_add = certificate or [] -- certificate_del = [] -- managedby_host_add = managedby_host or [] -- managedby_host_del = [] -- principal_add = principal or [] -- principal_del = [] -- allow_create_keytab_user_add = \ -- allow_create_keytab_user or [] -- allow_create_keytab_user_del = [] -- allow_create_keytab_group_add = \ -- allow_create_keytab_group or [] -- allow_create_keytab_group_del = [] -- allow_create_keytab_host_add = \ -- allow_create_keytab_host or [] -- allow_create_keytab_host_del = [] -- allow_create_keytab_hostgroup_add = \ -- allow_create_keytab_hostgroup or [] -- allow_create_keytab_hostgroup_del = [] -- allow_retrieve_keytab_user_add = \ -- allow_retrieve_keytab_user or [] -- allow_retrieve_keytab_user_del = [] -- allow_retrieve_keytab_group_add = \ -- allow_retrieve_keytab_group or [] -- allow_retrieve_keytab_group_del = [] -- allow_retrieve_keytab_host_add = \ -- allow_retrieve_keytab_host or [] -- allow_retrieve_keytab_host_del = [] -- allow_retrieve_keytab_hostgroup_add = \ -- allow_retrieve_keytab_hostgroup or [] -- allow_retrieve_keytab_hostgroup_del = [] -+ # IP addresses are not really a member of hosts, but -+ # we will simply treat it as this to enable the -+ # addition and removal of IPv4 and IPv6 addresses in -+ # a simple way. -+ _dnsrec = res_find_dnsrecord or {} -+ dnsrecord_a_add, dnsrecord_a_del = gen_add_del_lists( -+ dnsrecord_args.get("arecord"), -+ _dnsrec.get("arecord")) -+ dnsrecord_aaaa_add, dnsrecord_aaaa_del = \ -+ gen_add_del_lists( -+ dnsrecord_args.get("aaaarecord"), -+ _dnsrec.get("aaaarecord")) - -- else: -+ if action != "host" or (action == "host" and res_find is None): - certificate_add = certificate or [] - certificate_del = [] - managedby_host_add = managedby_host or [] -@@ -1001,6 +1036,10 @@ def main(): - allow_retrieve_keytab_hostgroup_add = \ - allow_retrieve_keytab_hostgroup or [] - allow_retrieve_keytab_hostgroup_del = [] -+ dnsrecord_a_add = dnsrecord_args.get("arecord") or [] -+ dnsrecord_a_del = [] -+ dnsrecord_aaaa_add = dnsrecord_args.get("aaaarecord") or [] -+ dnsrecord_aaaa_del = [] - - # Remove canonical principal from principal_del - canonical_principal = "host/" + name + "@" + server_realm -@@ -1135,6 +1174,36 @@ def main(): - "hostgroup": allow_retrieve_keytab_hostgroup_del, - }]) - -+ if len(dnsrecord_a_add) > 0 or len(dnsrecord_aaaa_add) > 0: -+ domain_name = name[name.find(".")+1:] -+ host_name = name[:name.find(".")] -+ -+ commands.append([domain_name, -+ "dnsrecord_add", -+ { -+ "idnsname": host_name, -+ "arecord": dnsrecord_a_add, -+ "a_extra_create_reverse": reverse, -+ "aaaarecord": dnsrecord_aaaa_add, -+ "aaaa_extra_create_reverse": reverse -+ }]) -+ -+ if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0: -+ domain_name = name[name.find(".")+1:] -+ host_name = name[:name.find(".")] -+ -+ # There seems to be an issue with dnsrecord_del (not -+ # for dnsrecord_add) if aaaarecord is an empty list. -+ # Therefore this is done differently here: -+ _args = {"idnsname": host_name} -+ if len(dnsrecord_a_del) > 0: -+ _args["arecord"] = dnsrecord_a_del -+ if len(dnsrecord_aaaa_del) > 0: -+ _args["aaaarecord"] = dnsrecord_aaaa_del -+ -+ commands.append([domain_name, -+ "dnsrecord_del", _args]) -+ - elif state == "absent": - if action == "host": - -@@ -1215,6 +1284,17 @@ def main(): - "hostgroup": allow_retrieve_keytab_hostgroup, - }]) - -+ dnsrecord_args = gen_dnsrecord_args(ansible_module, -+ ip_address, reverse) -+ if "arecord" in dnsrecord_args or \ -+ "aaaarecord" in dnsrecord_args: -+ domain_name = name[name.find(".")+1:] -+ host_name = name[:name.find(".")] -+ dnsrecord_args["idnsname"] = host_name -+ -+ commands.append([domain_name, "dnsrecord_del", -+ dnsrecord_args]) -+ - elif state == "disabled": - if res_find is not None: - commands.append([name, "host_disable", {}]) -@@ -1259,6 +1339,11 @@ def main(): - # Host is already disabled, ignore error - if "This entry is already disabled" in msg: - continue -+ -+ # Ignore no modification error. -+ if "no modifications to be performed" in msg: -+ continue -+ - ansible_module.fail_json(msg="%s: %s: %s" % (command, name, - msg)) - -diff --git a/tests/host/test_host_ipaddresses.yml b/tests/host/test_host_ipaddresses.yml -new file mode 100644 -index 0000000..0a97dd5 ---- /dev/null -+++ b/tests/host/test_host_ipaddresses.yml -@@ -0,0 +1,312 @@ -+--- -+- name: Test host IP addresses -+ hosts: ipaserver -+ become: true -+ -+ tasks: -+ - name: Get Domain from server name -+ set_fact: -+ ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}" -+ when: ipaserver_domain is not defined -+ -+ - name: Set host1_fqdn .. host6_fqdn -+ set_fact: -+ host1_fqdn: "{{ 'host1.' + ipaserver_domain }}" -+ host2_fqdn: "{{ 'host2.' + ipaserver_domain }}" -+ host3_fqdn: "{{ 'host3.' + ipaserver_domain }}" -+ -+ - name: Get IPv4 address prefix from server node -+ set_fact: -+ ipv4_prefix: "{{ ansible_default_ipv4.address.split('.')[:-1] | -+ join('.') }}" -+ -+ - name: Host absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: -+ - "{{ host1_fqdn }}" -+ - "{{ host2_fqdn }}" -+ - "{{ host3_fqdn }}" -+ update_dns: yes -+ state: absent -+ -+ - name: Host "{{ host1_fqdn }}" present -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.201' }}" -+ - fe80::20c:29ff:fe02:a1b2 -+ update_dns: yes -+ reverse: no -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.201' }}" -+ - fe80::20c:29ff:fe02:a1b2 -+ update_dns: yes -+ reverse: no -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present again with new IP address -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ update_dns: yes -+ reverse: no -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" present again with new IP address again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ update_dns: yes -+ reverse: no -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv4 address present -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: "{{ ipv4_prefix + '.201' }}" -+ action: member -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv4 address present again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: "{{ ipv4_prefix + '.201' }}" -+ action: member -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv4 address absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: "{{ ipv4_prefix + '.201' }}" -+ action: member -+ state: absent -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv4 address absent again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: "{{ ipv4_prefix + '.201' }}" -+ action: member -+ state: absent -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv6 address present -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: fe80::20c:29ff:fe02:a1b2 -+ action: member -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv6 address present again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: fe80::20c:29ff:fe02:a1b2 -+ action: member -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv6 address absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: fe80::20c:29ff:fe02:a1b2 -+ action: member -+ state: absent -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" member IPv6 address absent again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: fe80::20c:29ff:fe02:a1b2 -+ action: member -+ state: absent -+ register: result -+ -+ - name: Host "{{ host1_fqdn }}" member all ip-addresses absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ action: member -+ state: absent -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host1_fqdn }}" all member ip-addresses absent again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ action: member -+ state: absent -+ register: result -+ failed_when: result.changed -+ -+ - name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" present with same IP addresses -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ - name: "{{ host2_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" present with same IP addresses again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host1_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ - name: "{{ host2_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ register: result -+ failed_when: result.changed -+ -+ - name: Hosts "{{ host3_fqdn }}" present with same IP addresses -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host3_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Hosts "{{ host3_fqdn }}" present with same IP addresses again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host3_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host3_fqdn }}" present with differnt IP addresses -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host3_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.111' }}" -+ - fe80::20c:29ff:fe02:a1b1 -+ - "{{ ipv4_prefix + '.121' }}" -+ - fe80::20c:29ff:fe02:a1b2 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host3_fqdn }}" present with different IP addresses again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host3_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.111' }}" -+ - fe80::20c:29ff:fe02:a1b1 -+ - "{{ ipv4_prefix + '.121' }}" -+ - fe80::20c:29ff:fe02:a1b2 -+ register: result -+ failed_when: result.changed -+ -+ - name: Host "{{ host3_fqdn }}" present with old IP addresses -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host3_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ register: result -+ failed_when: not result.changed -+ -+ - name: Host "{{ host3_fqdn }}" present with old IP addresses again -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ hosts: -+ - name: "{{ host3_fqdn }}" -+ ip_address: -+ - "{{ ipv4_prefix + '.211' }}" -+ - fe80::20c:29ff:fe02:a1b3 -+ - "{{ ipv4_prefix + '.221' }}" -+ - fe80::20c:29ff:fe02:a1b4 -+ register: result -+ failed_when: result.changed -+ -+ - name: Host absent -+ ipahost: -+ ipaadmin_password: MyPassword123 -+ name: -+ - "{{ host1_fqdn }}" -+ - "{{ host2_fqdn }}" -+ - "{{ host3_fqdn }}" -+ update_dns: yes -+ state: absent -From 8f32cb04c1e161e1e3217f10413685a2cc9bf492 Mon Sep 17 00:00:00 2001 -From: Thomas Woerner -Date: Thu, 13 Feb 2020 14:10:38 +0100 -Subject: [PATCH] tests/host/test_host: Fix use of wrong host in the host5 test - -host1 was used instead of host5 in the repeated host5 test. This lead to an -error with the new IP address handling in ipahost. It was correctly -reporting a change for host1 which resulted in a failed test. ---- - tests/host/test_host.yml | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/host/test_host.yml b/tests/host/test_host.yml -index 1a555a1..f3ec11d 100644 ---- a/tests/host/test_host.yml -+++ b/tests/host/test_host.yml -@@ -129,7 +129,7 @@ - - name: Host "{{ host5_fqdn }}" present again - ipahost: - ipaadmin_password: MyPassword123 -- name: "{{ host1_fqdn }}" -+ name: "{{ host5_fqdn }}" - ip_address: "{{ ipv4_prefix + '.205' }}" - update_dns: yes - reverse: no diff --git a/SPECS/ansible-freeipa.spec b/SPECS/ansible-freeipa.spec index 0b748d4..fe3f85d 100644 --- a/SPECS/ansible-freeipa.spec +++ b/SPECS/ansible-freeipa.spec @@ -5,28 +5,33 @@ Summary: Roles and playbooks to deploy FreeIPA servers, replicas and clients Name: ansible-freeipa -Version: 0.1.8 -Release: 3%{?dist} +Version: 0.1.12 +Release: 6%{?dist} URL: https://github.com/freeipa/ansible-freeipa License: GPLv3+ Source: https://github.com/freeipa/ansible-freeipa/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz -Patch1: ansible-freeipa-0.1.8-ipahost-Fix-choices-of-auth_ind-parameter-allow-to-reset-parameter_rhbz#1783992.patch -Patch2: ansible-freeipa-0.1.8-ipauser-Allow-reset-of-userauthtype-do-not-depend-on-first-last-for-mod_rhbz#1784474.patch -Patch3: ansible-freeipa-0.1.8-ipahost-Enhanced-failure-msg-for-member-params-used-without-member-action_rhbz#1783948.patch -Patch4: ansible-freeipa-0.1.8-Add-missing-attributes-to-ipasudorule_rhbz#1788168,1788035,1788024.patch -Patch5: ansible-freeipa-0.1.8-ipapwpolicy-Use-global_policy-if-name-is-not-set_rhbz#1797532.patch -Patch6: ansible-freeipa-0.1.8-ipahbacrule-Fix-handing-of-members-with-action-hbacrule_rhbz#1787996.patch -Patch7: ansible-freeipa-0.1.8-ansible_freeipa_module-Fix-comparison-of-bool-parameters-in-compare_args_ipa_rhbz#1784514.patch -Patch8: ansible-freeipa-ipahost-Add-support-for-several-IP-addresses-and-also-to-change-them_rhbz#1783979,1783976.patch -Patch9: ansible-freeipa-0.1.8-ipahost-Fail-on-action-member-for-new-hosts-fix-dnsrecord_add-reverse-flag_rhbz#1803026.patch -Patch10: ansible-freeipa-0.1.8-ipahost-Do-not-fail-on-missing-DNS-or-zone-when-no-IP-address-given_rhbz#1804838.patch +Patch1: ansible-freeipa-0.1.12-Fixes-service-disable-when-service-has-no-certificates-attached_rhbz#1836294.patch +Patch2: ansible-freeipa-0.1.12-Add-suppport-for-changing-password-of-symmetric-vaults_rhbz#1839197.patch +Patch3: ansible-freeipa-0.1.12-Fix-forwardzone-issues_rhbz#1843826,1843828,1843829,1843830,1843831.patch +Patch4: ansible-freeipa-0.1.12-ipa-host-group-Fix-membermanager-unknow-user-issue_rhbz#1848426.patch +Patch5: ansible-freeipa-0.1.12-ipa-user,host-Fail-on-duplucate-names-in-the-users-and-hosts-lists_rhbz#1822683.patch +Patch6: ansible-freeipa-0.1.12-action_plugins-ipaclient_get_otp-Discovered-python-n_rhbz#1852714.patch +Patch7: ansible-freeipa-0.1.12-ipa-server-replica-Fix-pkcs12-info-regressions-intro_rhbz#1853284.patch +Patch8: ansible-freeipa-0.1.12-ipareplica-Fix-missing-parameters-for-several-module_hbz#1855299.patch +Patch9: ansible-freeipa-0.1.12-Allow-multiple-dns-zones-to-be-absent_rhbz#1845058.patch +Patch10: ansible-freeipa-0.1.12-Fixed-error-msgs-on-FreeIPABaseModule-subclasses_rhbz#1845051.patch +Patch11: ansible-freeipa-0.1.12-Fix-allow_retrieve_keytab_host-in-service-module_rhbz#1868020.patch +Patch12: ansible-freeipa-0.1.12-Modified-return-value-for-ipavault-module_rhbz#1867909.patch +Patch13: ansible-freeipa-0.1.12-Add-support-for-option-name_from_ip-in-ipadnszone-mo_rhbz#1845056.patch +Patch14: ansible-freeipa-0.1.12-Fixes-password-behavior-on-Vault-module_rhbz#1839200.patch BuildArch: noarch #Requires: ansible %description ansible-freeipa provides Ansible roles and playbooks to install and uninstall -FreeIPA servers, replicas and clients also modules for management. +FreeIPA servers, replicas and clients. Also modules for group, host, topology +and user management. Note: The ansible playbooks and roles require a configured ansible environment where the ansible nodes are reachable and are properly set up to have an IP @@ -38,6 +43,9 @@ Features - Cluster deployments: Server, replicas and clients in one playbook - One-time-password (OTP) support for client installation - Repair mode for clients +- Modules for dns forwarder management +- Modules for dns record management +- Modules for dns zone management - Modules for group management - Modules for hbacrule management - Modules for hbacsvc management @@ -45,11 +53,13 @@ Features - Modules for host management - Modules for hostgroup management - Modules for pwpolicy management +- Modules for service management - Modules for sudocmd management - Modules for sudocmdgroup management - Modules for sudorule management - Modules for topology management - Modules for user management +- Modules for vault management Supported FreeIPA Versions @@ -69,23 +79,25 @@ Supported Distributions Requirements Controller + - Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection) - - /usr/bin/kinit is required on the controller if a one time password (OTP) + /usr/bin/kinit is required on the controller if a one time password (OTP) is used - python3-gssapi is required on the controller if a one time password (OTP) is used with keytab to install the client. Node + - Supported FreeIPA version (see above) - Supported distribution (needed for package installation only, see above) Limitations -External CA support is not supported or working. The currently needed two step -process is an issue for the processing in the role. The configuration of the -server is partly done already and needs to be continued after the CSR has been -handled. This is for example breaking the deployment of a server with replicas -or clients in one playbook. +External signed CA is now supported. But the currently needed two step process +is an issue for the processing in a simple playbook. +Work is planned to have a new method to handle CSR for external signed CAs in +a separate step before starting the server installation. + %prep %setup -q @@ -100,6 +112,10 @@ or clients in one playbook. %patch8 -p1 %patch9 -p1 %patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 # Fix python modules and module utils: # - Remove shebang # - Remove execute flag @@ -130,11 +146,84 @@ cp -rp plugins/* %{buildroot}%{_datadir}/ansible/plugins/ %{_datadir}/ansible/roles/ipaclient %{_datadir}/ansible/plugins/module_utils %{_datadir}/ansible/plugins/modules -%doc README.md -%doc README-*.md +%doc README*.md %doc playbooks %changelog +* Tue Aug 18 2020 Thomas Woerner - 0.1.12-6 +- Allow to manage multiple dnszone entries + Resolves: RHBZ#1845058 +- Fixed error msgs on FreeIPABaseModule subclasses + Resolves: RHBZ#1845051 +- Fix `allow_create_keytab_host` in service module + Resolves: RHBZ#1868020 +- Modified return value for ipavault module + Resolves: RHBZ#1867909 +- Add support for option `name_from_ip` in ipadnszone module + Resolves: RHBZ#1845056 +- Fixe password behavior on Vault module + Resolves: RHBZ#1839200 + +* Tue Jul 14 2020 Thomas Woerner - 0.1.12-5 +- ipareplica: Fix failure while deploying KRA + Resolves: RHBZ#1855299 + +* Thu Jul 02 2020 Thomas Woerner - 0.1.12-4 +- ipa[server,replica]: Fix pkcs12 info regressions introduced with CA-less + Resolves: RHBZ#1853284 + +* Wed Jul 01 2020 Thomas Woerner - 0.1.12-3 +- action_plugins/ipaclient_get_otp: Discovered python needed in task_vars + Resolves: RHBZ#1852714 + +* Mon Jun 29 2020 Thomas Woerner - 0.1.12-2 +- Fixes service disable when service has no certificates attached + Resolves: RHBZ#1836294 +- Add suppport for changing password of symmetric vaults + Resolves: RHBZ#1839197 +- Fix forwardzone issues + Resolves: RHBZ#1843826 + Resolves: RHBZ#1843828 + Resolves: RHBZ#1843829 + Resolves: RHBZ#1843830 + Resolves: RHBZ#1843831 +- ipa[host]group: Fix membermanager unknow user issue + Resolves: RHBZ#1848426 +- ipa[user,host]: Fail on duplucate names in the users and hosts lists + Resolves: RHBZ#1822683 + +* Mon Jun 15 2020 Thomas Woerner - 0.1.12-1 +- Update to version 0.1.12 bug fix only release + Related: RHBZ#1818768 + +* Thu Jun 11 2020 Thomas Woerner - 0.1.11-1 +- Update to version 0.1.11 + Related: RHBZ#1818768 + +* Mon Apr 27 2020 Thomas Woerner - 0.1.10-1 +- Update to version 0.1.10: + - ipaclient: Not delete keytab when ipaclient_on_master is true + - New module to manage dns forwarder zones in ipa + - Enhancements of sudorule module tests + - Gracefully handle RuntimeError raised during parameter validation in + fail_jso + - ipareplica_prepare: Fix module DOCUMENTATION + - ipa[server,replica,client]: setup_logging wrapper for + standard_logging_setup + - Created FreeIPABaseModule class to facilitate creation of new modules + - New IPADNSZone module + - Add admin password to the ipadnsconfig module tests + - Added alias module arguments in dnszone module + - Fixed a bug in AnsibleFreeIPAParams + - utils/build-galaxy-release: Do not add release tag to version for galaxy + - ipaserver docs: Calm down module linter + - galaxy.yml: Add system tag + - ipareplica_setup_kra: Remove unused ccache parameter + - ipareplica_setup_krb: krb is assigned to but never used + - utils/galaxy: Make galaxy scripts more generic + - galaxyfy-playbook.py: Fixed script name + Related: RHBZ#1818768 + * Thu Feb 20 2020 Thomas Woerner - 0.1.8-3 - ipahost: Do not fail on missing DNS or zone when no IP address given Resolves: RHBZ#1804838