import ansible-freeipa-0.3.2-2.el8

This commit is contained in:
CentOS Sources 2021-05-18 02:48:35 -04:00 committed by Stepan Oksanichenko
parent d2eec6cc08
commit 72314a0ec3
18 changed files with 179 additions and 4921 deletions

View File

@ -1 +1 @@
5d09d3b590e8568d04edb288c9c515e308f3168f SOURCES/ansible-freeipa-0.1.12.tar.gz
7c66c505597de97501d68c81fc1495aa4d627879 SOURCES/ansible-freeipa-0.3.2.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/ansible-freeipa-0.1.12.tar.gz
SOURCES/ansible-freeipa-0.3.2.tar.gz

View File

@ -1,556 +0,0 @@
From abbd15e6f50718119b4dd0380913d2d646eb7638 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
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
&nbsp; | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
&nbsp; | `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 <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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`. <br>Options: | If `state` is `present`, `name_from_ip` is used, and a zone was created.
+&nbsp; | `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 <rjeffman@redhat.com>
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
&nbsp; | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
&nbsp; | `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

View File

@ -1,435 +0,0 @@
From 78b635ae78346fdfb298dd0d0c82ae1ff34b754a Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
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

View File

@ -1,302 +0,0 @@
From 75d16c2da4a5621943873a26343eb0f2acc2a925 Mon Sep 17 00:00:00 2001
From: Sergio Oliveira Campos <seocam@seocam.com>
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
&nbsp; | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
&nbsp; | `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

View File

@ -1,628 +0,0 @@
# 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 <rjeffman@redhat.com>
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

View File

@ -1,40 +0,0 @@
From 563a03d94bfc29799ea964dab61a1ba35818b9bb Mon Sep 17 00:00:00 2001
From: Sergio Oliveira Campos <seocam@seocam.com>
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

View File

@ -1,327 +0,0 @@
From 3e5c54d4fdb10deda9b7e4deaf2c537b132711c9 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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

View File

@ -1,112 +0,0 @@
From e57e4908f936c524085fb5853fe4493c7711ab3f Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
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 }}"

View File

@ -1,300 +0,0 @@
From e96ef4e98e523f20c25777308c093ebbff272b2d Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
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 <rjeffman@redhat.com>
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) <br>Options: | If `state` is `retrieved`.
+&nbsp; | `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

View File

@ -1,49 +0,0 @@
From 80aac15de9026055ae2b9972859939cf7925b813 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
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

View File

@ -1,132 +0,0 @@
From 6132a947e65fb9c3a1ec5c059aed34afb06a67df Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
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

View File

@ -1,150 +0,0 @@
From 8ce5fd147aafc34e43dbe4246565c48eace2e115 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
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

View File

@ -1,132 +0,0 @@
From 1d7fb31b8bfa00babd7c753b354d7344b531cd77 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
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

View File

@ -1,271 +0,0 @@
From 7a2eaa6f535b1353d46bcfa8b0b2484b15ff3863 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
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

View File

@ -0,0 +1,39 @@
From 976cd1baa70b3ac1a271a362163e469b8d54d04a Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Mon, 22 Feb 2021 13:28:04 +0100
Subject: [PATCH] ipaclient: Do not fail on rmkeytab error #7
Due to commit f3f9672d527008dc741ac90aa465bac842eea08d (ipa-rmkeytab: Check
return value of krb5_kt_(start|end)_seq_get) in IPA 4.9.2 there is a new
error reported for ipa-rmkeytab in case of a non existing keytab file.
Using ipa-rmkeytab now results in the error #7 in this case.
The client role is using ipa-rmkeytab and needs to ignore error #7 also.
Fixes: #510 (ipa-client installation with OTP is failed with error code 7
(keytab: /usr/sbin/ipa-rmkeytab returned 7))
---
roles/ipaclient/tasks/install.yml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml
index fccc72e..23f9529 100644
--- a/roles/ipaclient/tasks/install.yml
+++ b/roles/ipaclient/tasks/install.yml
@@ -181,8 +181,12 @@
# Do not fail on error codes 3 and 5:
# 3 - Unable to open keytab
# 5 - Principal name or realm not found in keytab
+ # 7 - Failed to set cursor, typically when errcode
+ # would be issued in past
failed_when: result_ipa_rmkeytab.rc != 0 and
- result_ipa_rmkeytab.rc != 3 and result_ipa_rmkeytab.rc != 5
+ result_ipa_rmkeytab.rc != 3 and
+ result_ipa_rmkeytab.rc != 5 and
+ result_ipa_rmkeytab.rc != 7
when: (ipaclient_use_otp | bool or ipaclient_force_join | bool) and not ipaclient_on_master | bool
- name: Install - Backup and set hostname
--
2.29.2

View File

@ -1,37 +1,24 @@
# Turn off automatic python byte compilation because these are Ansible
# roles and the files are transferred to the node and compiled there with
# the python verison used in the node
# the python version used in the node
%define __brp_python_bytecompile %{nil}
%global python %{__python3}
Summary: Roles and playbooks to deploy FreeIPA servers, replicas and clients
Name: ansible-freeipa
Version: 0.1.12
Release: 6%{?dist}
Version: 0.3.2
Release: 2%{?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.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
Patch0: ansible-freeipa-0.3.2-ipaclient-Do-not-fail-on-rmkeytab-error-7_rhbz#1931381.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 group, host, topology
and user management.
FreeIPA servers, replicas and clients. Also modules for 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
@ -43,6 +30,10 @@ Features
- Cluster deployments: Server, replicas and clients in one playbook
- One-time-password (OTP) support for client installation
- Repair mode for clients
- Backup and restore, also to and from controller
- Modules for config management
- Modules for delegation management
- Modules for dns config management
- Modules for dns forwarder management
- Modules for dns record management
- Modules for dns zone management
@ -52,12 +43,18 @@ Features
- Modules for hbacsvcgroup management
- Modules for host management
- Modules for hostgroup management
- Modules for location management
- Modules for permission management
- Modules for privilege management
- Modules for pwpolicy management
- Modules for role management
- Modules for self service management
- Modules for service management
- Modules for sudocmd management
- Modules for sudocmdgroup management
- Modules for sudorule management
- Modules for topology management
- Modules fot trust management
- Modules for user management
- Modules for vault management
@ -99,32 +96,36 @@ Work is planned to have a new method to handle CSR for external signed CAs in
a separate step before starting the server installation.
%package tests
Summary: ansible-freeipa tests
Requires: %{name} = %{version}-%{release}
%description tests
ansible-freeipa tests.
Please have a look at %{_datadir}/ansible-freeipa/requirements-tests.txt
to get the needed requrements to run the tests.
%prep
%setup -q
%patch0 -p1
# Do not create backup files with patches
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
%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
for i in roles/ipa*/library/*.py roles/ipa*/module_utils/*.py plugins/*/*.py; do
sed -i '/\/usr\/bin\/python*/d' $i
sed -i '1{/\/usr\/bin\/python*/d;}' $i
chmod a-x $i
done
# Add execute flag to py3test.py scripts
chmod a+x roles/ipa*/files/py3test.py
for i in utils/*.py utils/ansible-ipa-*-install utils/new_module \
utils/changelog utils/ansible-doc-test;
do
sed -i '{s@/usr/bin/python*@%{python}@}' $i
done
%build
@ -136,20 +137,119 @@ cp -rp roles/ipareplica %{buildroot}%{_datadir}/ansible/roles/
cp -rp roles/ipareplica/README.md README-replica.md
cp -rp roles/ipaclient %{buildroot}%{_datadir}/ansible/roles/
cp -rp roles/ipaclient/README.md README-client.md
cp -rp roles/ipabackup %{buildroot}%{_datadir}/ansible/roles/
cp -rp roles/ipabackup/README.md README-backup.md
install -m 755 -d %{buildroot}%{_datadir}/ansible/plugins/
cp -rp plugins/* %{buildroot}%{_datadir}/ansible/plugins/
install -m 755 -d %{buildroot}%{_datadir}/ansible-freeipa
cp requirements*.txt %{buildroot}%{_datadir}/ansible-freeipa/
cp -rp utils %{buildroot}%{_datadir}/ansible-freeipa/
install -m 755 -d %{buildroot}%{_datadir}/ansible-freeipa/tests
cp -rp tests %{buildroot}%{_datadir}/ansible-freeipa/
%files
%license COPYING
%{_datadir}/ansible/roles/ipaserver
%{_datadir}/ansible/roles/ipareplica
%{_datadir}/ansible/roles/ipaclient
%{_datadir}/ansible/roles/ipabackup
%{_datadir}/ansible/plugins/module_utils
%{_datadir}/ansible/plugins/modules
%doc README*.md
%doc playbooks
%{_datadir}/ansible-freeipa/requirements.txt
%{_datadir}/ansible-freeipa/requirements-dev.txt
%{_datadir}/ansible-freeipa/utils
%files tests
%{_datadir}/ansible-freeipa/tests
%{_datadir}/ansible-freeipa/requirements-tests.txt
%changelog
* Thu Mar 4 2021 Thomas Woerner <twoerner@redhat.com> - 0.3.2-2
- Fix ipaclient: Do not fail on rmkeytab error 7
Resolves: RHBZ#1931381
* Mon Jan 18 2021 Thomas Woerner <twoerner@redhat.com> - 0.3.2-1
- Update to version 0.3.2
https://github.com/freeipa/ansible-freeipa/releases/tag/v0.3.2
Related: RHBZ#1891826
- Not able to add additional privileges with existing privilege in role module
Resolves: RHBZ#1893678
- Required error message while adding non-existing members in role handling
Resolves: RHBZ#1893679
- Not able to add new members with existing members role handling
Resolves: RHBZ#1893684
- service members are removed while updating other members in role handling
Resolves: RHBZ#1893685
- after changing the vault type from standard to symmetric, Salt is missing
Resolves: RHBZ#1880367
- After changing the vault type from symmetric to asymmetric, Salt is present
in the asymmetric vault
Resolves: RHBZ#1880377
- After changing the vault type from asymmetric to the standard vault, the
Public key is present in the standard vault
Resolves: RHBZ#1880378
- Not able to replace public-key-file to the public-key in asymmetric vault
type
Resolves: RHBZ#1880862
- ipauser module does not seem to support --check flag to ansible-playbook
Resolves: RHBZ#1893675
- Not able to add additional attributes with existing attributes in permission
handling
Resolves: RHBZ#1893687
- Privilege variable is removed from permission handling
Resolves: RHBZ#1893688
* Wed Dec 2 2020 Thomas Woerner <twoerner@redhat.com> - 0.3.1-1
- Update to version 0.3.1
https://github.com/freeipa/ansible-freeipa/releases/tag/v0.3.1
Related: RHBZ#1891826
- ipabackup: Fix undefined vars for conditions in shell tasks without else
Related: RHBZ#1894494
* Tue Dec 1 2020 Thomas Woerner <twoerner@redhat.com> - 0.3.0-2
- Ship ipabackup role for backup and restore
Related: RHBZ#1894494
* Thu Nov 26 2020 Thomas Woerner <twoerner@redhat.com> - 0.3.0-1
- Update to version 0.3.0
https://github.com/freeipa/ansible-freeipa/releases/tag/v0.3.0
With tests sub package
Resolves: RHBZ#1891826
- Support for firewalld zone in ipaserver and ipareplica roles
Resolves: RHBZ#1894488
- ipagroup: Add support for the IPA CLI option `posix`
Resolves: RHBZ#1894493
- New ipabackup role for backup and restore
Resolves: RHBZ#1894494
- New management module ipadelegation
Resolves: RHBZ#1894496
- New management module ipalocation
Resolves: RHBZ#1894497
- New management module ipaprivilege
Resolves: RHBZ#1894498
- New management module ipapermission
Resolves: RHBZ#1894499
- New management module iparole
Resolves: RHBZ#1894500
- New management module ipaselfservice
Resolves: RHBZ#1894501
- New management module ipatrust
Resolves: RHBZ#1894502
- Fixed log of vault data return when retrieving to a file
Resolves: RHBZ#1875378
- ipadnszone: Fix modification o SOA serial with other attributes
Resolves: RHBZ#1876896
- Fix symmetric vault password change when using password_files
Resolves: RHBZ#1879004
- ipadnsrecord: fix record modification behavior
Resolves: RHBZ#1880409
Resolves: RHBZ#1881452
- ipadnsrecord: fix record update when multiple records exist
Resolves: RHBZ#1881436
* Tue Aug 18 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-6
- Allow to manage multiple dnszone entries
Resolves: RHBZ#1845058