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-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-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-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/SPECS/ansible-freeipa.spec b/SPECS/ansible-freeipa.spec index a6d4b0b..fe3f85d 100644 --- a/SPECS/ansible-freeipa.spec +++ b/SPECS/ansible-freeipa.spec @@ -6,7 +6,7 @@ Summary: Roles and playbooks to deploy FreeIPA servers, replicas and clients Name: ansible-freeipa Version: 0.1.12 -Release: 5%{?dist} +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 @@ -18,6 +18,12 @@ Patch5: ansible-freeipa-0.1.12-ipa-user,host-Fail-on-duplucate-names-in-the-user 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 @@ -104,6 +110,12 @@ a separate step before starting the server installation. %patch6 -p1 %patch7 -p1 %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 @@ -138,6 +150,20 @@ cp -rp plugins/* %{buildroot}%{_datadir}/ansible/plugins/ %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