Compare commits

..

41 Commits

Author SHA1 Message Date
Thomas Woerner
fa9f100350 Fix typo in README-permission.md
There is a typo "Eure" instead of "Ensure" in the rename task.
2021-01-11 12:21:30 +01:00
Rafael Guterres Jeffman
17c7872a8b Merge pull request #484 from t-woerner/permission_fix_attrs_drop_privilege
ipapermission: Fix attrs and drop privilege handling
2021-01-08 16:12:01 -03:00
Thomas Woerner
69b045322d Merge pull request #476 from rjeffman/fix_ipadnszone_allow_tranfers_networks
ipadnszone: Fix values accepted by allow_transfer and allow_query.
2021-01-08 14:17:23 +01:00
Thomas Woerner
a1f385f017 Merge pull request #472 from rjeffman/testinfra_update
Change test requirement testinfra to pytest-testinfra.
2021-01-08 13:59:37 +01:00
Thomas Woerner
23829c5ec4 ipapermission: Fix attrs and drop privilege handling
The attrs handling was not complete and did not support to ensure presence
or absence of attributes with action:member.

The includedattrs and excludedattrs parameters have not been added with
this change as the use of attrs will automatically set includedattrs and
excludedattrs. The includedattrs and excludedattrs parameters are only
usable for managed permissions and duplicating attrs.

The permission module may not handle privileges. An IPA internal only API
has been used for this. The prvilege variable and all related code paths
have been removed.

Fixes: #424 ([Permission Handling] Not able to add additional attributes
             with existing attributes)
Fixes: #425 ([Permission Handling] Not able to add member privilege while
             adding permission)
2021-01-08 13:49:34 +01:00
Thomas Woerner
11e5a2867e Merge pull request #468 from rjeffman/fix_vault_change_type
Fix changing the type of an existing Vault.
2021-01-07 15:15:58 +01:00
Thomas Woerner
27a805313e Merge pull request #469 from rjeffman/fix_role_add_privileges
Fix handling members in ipa role.
2021-01-07 15:13:30 +01:00
Thomas Woerner
29dc21a40c Merge pull request #478 from enothen/master
Update modules to support check_mode
2021-01-07 15:08:53 +01:00
Rafael Guterres Jeffman
14f682ad76 Remove usage of b64encode in lookup from Vault tests.
There are some issues using a combination of `lookup('file')` and the
`b64encode` filter in Ansible, making tests unstable. This change
removes the usage of b64encode when loading public and private keys
from files in the Vault test playbooks.
2021-01-07 09:18:53 -03:00
Eric Nothen
7bbb401b9b Enabled Ansible check_mode
Added code to the ipa* plugins to support Ansible's check_mode, by
means of a clean exit before the execution of the actual list of
commands that would otherwise create/update/delete IPA servers
and/or its resources.
2021-01-06 12:18:35 +01:00
Rafael Guterres Jeffman
7e04a46f07 Fix changing the type of an existing Vault.
Current implementation does not allow the change of an existingi Vault
type. To allow it, data is retrieved from the current vault, the vault
is modifiend, and then, data is stored again in the new vault.

Due to changing the process of modifying a vault, this change also
fixes the update of asymmetric vault keys. To change the key used,
the task must provide the old private key, used to retrieve data,
and the new public_key, used to store the data again. A new alias
was added to public_key (new_public_key) and public_key_file
(new_public_key_file) so that the playbook better express the
intention of the tak.

Vault tests have been updated to better test against the new update
process, and a new test file has bee added:

    tests/vault/test_vault_change_type.
2021-01-04 11:11:22 -03:00
Rafael Guterres Jeffman
6f0d183aba ipadnszone: Fix values accepted by allow_transfer and allow_query.
In FreeIPA CLI, The attributes `allow_query` and `allow_transfer` can
hold IPv4 or IPv6 address or network address, and the values `none` and
`any`.

This patch adds support for network addresses, `none` and `any`, which
were not supported.

Fix issue #475.
2020-12-29 12:39:47 -03:00
Rafael Guterres Jeffman
67179a8c4b Fix handling members in ipa role.
When adding new members to a role, the existing members were removed.
The correct behavior for the "member" action is to add those members,
and substitute the existing ones. This patch fixes this behavior.

Fix #409, #411, #412, #413
2020-12-22 11:42:42 -03:00
Rafael Guterres Jeffman
04e95cfa1e Change test requirement testinfra to pytest-testinfra.
According to the testinfra changelog, since version 6.0.0, testinfra
is know as pytest-testinfra, and the use of testinfra is deprecated.
This change will prevent future isses when updating requirements using
`pip`.

Ref: https://testinfra.readthedocs.io/en/latest/changelog.html
2020-12-22 11:39:41 -03:00
Thomas Woerner
8d9e794ddf Merge pull request #473 from nphilipp/master--typo
Fix typo
2020-12-22 15:38:16 +01:00
Thomas Woerner
8fc2e6cbb2 Merge pull request #470 from rjeffman/tools_speed_up_commit
Faster pre-commit by running ansible-lint only when necessary.
2020-12-22 15:31:23 +01:00
Thomas Woerner
5634f94efb Merge pull request #471 from rjeffman/tools_flake8_bugbear
Tools flake8 bugbear
2020-12-22 15:29:07 +01:00
Nils Philippsen
0a3e13b0c3 Fix typo
Signed-off-by: Nils Philippsen <nils@redhat.com>
2020-12-21 14:09:02 +01:00
Rafael Guterres Jeffman
97b06ff6f0 Update configuration to use flake8-bugbear.
Bugbear is a plugin for Flake8 finding likely bugs and design problems.
It contain warnings that don't belong in pyflakes and pycodestyle, and
do not have a PEP or standard behind them.

Ref: https://github.com/PyCQA/flake8-bugbear
2020-12-16 18:16:47 -03:00
Rafael Guterres Jeffman
f89330a80d Use Python Linter action with support for flake8's bugbear. 2020-12-15 19:02:44 -03:00
Rafael Guterres Jeffman
ba697466a3 [flake8-bugbear] Fix unused loop variable.
This commit change the name of a variable to make it more clear that it
is not required in the for-loop, removing a bugbear B007 warning.
2020-12-15 19:02:44 -03:00
Rafael Guterres Jeffman
7415280728 [flake8-bugbear] Fix unused loop variable.
Running flake8 with bugbear enable found an extra for-loop that is not
needed. The for-loop was removed, fixing bubear's warning.
2020-12-15 19:02:44 -03:00
Rafael Guterres Jeffman
3d4affcbf9 Faster pre-commit by running ansible-lint only when necessary.
This patch disables ansible-lint `always_run` flag, as this was
making patches that did not change any YAML file take longer in
the pre-commit step, as ansible-lint was executed with no parameter,
thus, searching and evaluating all YAML files in the repository.

With this change, if no YAML file is modified, ansible-lint is skipped.
2020-12-15 17:19:58 -03:00
Thomas Woerner
eba38e30a3 Merge pull request #466 from rjeffman/utils_fix_covscan_findings_lint_check
covscan error[SC2068]: Fix unquoted array expansions.
2020-12-10 09:56:16 +01:00
Rafael Guterres Jeffman
bc4564876b Merge pull request #465 from t-woerner/gen_module_docs_fix_covsvan_findings
utils/gen_modules_docs.sh: Fix covscan findings
2020-12-09 13:21:38 -03:00
Rafael Guterres Jeffman
cef733eba2 covscan error[SC2068]: Fix unquoted array expansions.
error[SC2068]: Double quote array expansions to avoid re-splitting elements.
2020-12-09 13:13:52 -03:00
Rafael Guterres Jeffman
85bd3f5f20 Merge pull request #464 from t-woerner/new_module_fix_covsvan_findings
utils/new_module: Fix covscan findings
2020-12-09 12:16:14 -03:00
Rafael Guterres Jeffman
8444e89640 Merge pull request #463 from t-woerner/build-galaxy-release_fix_covsvan_findings
utils/build-galaxy-release.sh: Fix covscan findings
2020-12-09 12:15:43 -03:00
Thomas Woerner
0cfc9d0147 utils/gen_modules_docs.sh: Fix covscan findings
error[SC2148]: Tips depend on target shell and yours is unknown.
  Add a shebang.
2020-12-09 16:02:08 +01:00
Thomas Woerner
18c195b052 utils/new_module: Fix covscan findings
warning[SC2166]: Prefer [ p ] || [ q ] as [ p -o q ] is not well
  defined.
2020-12-09 15:57:42 +01:00
Thomas Woerner
c0321b433b utils/build-galaxy-release.sh: Fix covscan findings
warning[SC2044]: For loops over find output are fragile. Use find -exec
  or a while read loop.
warning[SC2164]: Use 'cd ... || exit' or 'cd ... || return' in case cd
  fails.
2020-12-09 15:44:54 +01:00
Thomas Woerner
e2f3941512 Merge pull request #455 from rjeffman/lint_yamllint_only_modified
yamllint: Run yaml linter only on modified files in pre-commit.
2020-12-08 10:21:56 +01:00
Thomas Woerner
3802e494ef Merge pull request #461 from t-woerner/fix_ipabackup_shell_vars_no_else
ipabackup: Fix undefined vars for conditions in shell tasks without else
2020-12-02 13:45:03 +01:00
Thomas Woerner
923208b98c ipabackup: Fix undefined vars for conditions in shell tasks without else
The use of conditions in shell tasks without else clause is failing on
some systems with an undefined variable error.
2020-12-01 14:50:46 +01:00
Rafael Guterres Jeffman
06d73ba8df Merge pull request #460 from t-woerner/build-galaxy-release_args
utils/build-galaxy-release.sh: Fix default namespace and collection name
2020-11-30 12:09:37 -03:00
Rafael Guterres Jeffman
6f27ce6e22 Merge pull request #459 from t-woerner/changelog_get_commit
utils/changelog: Fix get_commit to use proper variable
2020-11-30 12:07:26 -03:00
Thomas Woerner
4d6023207e utils/build-galaxy-release.sh: Fix default namespace and collection name
The default namespace and collection name was not set due to using ":"
instead of "-" while setting the variables internally.
2020-11-30 16:05:58 +01:00
Thomas Woerner
dff485cb7e utils/changelog: Fix get_commit to use proper variable
The function get_commit was using the global merge variable instead of
the local commit variable. Therefore it returned the wrong commit
subject for merges without subject.
2020-11-30 15:51:33 +01:00
Rafael Guterres Jeffman
1647149808 Merge pull request #458 from t-woerner/ipareplica_fix_no_dnssec_validation
ipareplica: Fix no_dnssec_validation handling in prepare and setup_dns
2020-11-27 14:24:43 -03:00
Thomas Woerner
21a54dc732 ipareplica: Fix no_dnssec_validation handling in prepare and setup_dns
The parameter options.no_dnssec_validation was set using a bad
parameter name. This lead to not beeing able to turn off dnssec
validation in the replica deployment.

Fixes: #456 (ipareplica_no_dnssec_validation)
2020-11-27 15:58:48 +01:00
Rafael Guterres Jeffman
1ac93cb736 yamllint: Run yaml linter only on modified files in pre-commit.
With the parameter `args: ['.']`, yamllint would run over every
file during pre-commit, including those not being commited, and it
would allow for false negatives, not allowing a commit, even if
commited yaml files had no issues, but another file, not par of the
commit, had.

By changing the yamllint parameter to `files: \.(yaml|yml)$` it
will only check files being commited, preventing false negatives,
and allowing for faster commits.
2020-11-26 18:34:44 -03:00
57 changed files with 1339 additions and 399 deletions

View File

@@ -30,4 +30,4 @@ jobs:
uses: ibiqlik/action-yamllint@v1 uses: ibiqlik/action-yamllint@v1
- name: Run Python linters - name: Run Python linters
uses: rjeffman/python-lint-action@master uses: rjeffman/python-lint-action@v2

View File

@@ -4,7 +4,7 @@ repos:
rev: v4.3.5 rev: v4.3.5
hooks: hooks:
- id: ansible-lint - id: ansible-lint
always_run: true always_run: false
pass_filenames: true pass_filenames: true
files: \.(yaml|yml)$ files: \.(yaml|yml)$
entry: env ANSIBLE_LIBRARY=./plugins/modules ANSIBLE_MODULE_UTILS=./plugins/module_utils ansible-lint --force-color entry: env ANSIBLE_LIBRARY=./plugins/modules ANSIBLE_MODULE_UTILS=./plugins/module_utils ansible-lint --force-color
@@ -12,7 +12,7 @@ repos:
rev: v1.25.0 rev: v1.25.0
hooks: hooks:
- id: yamllint - id: yamllint
args: ['.'] files: \.(yaml|yml)$
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4 rev: 3.8.4
hooks: hooks:

View File

@@ -43,7 +43,7 @@ Example playbook to make sure permission "MyPermission" is present:
```yaml ```yaml
--- ---
- name: Playbook to create an IPA permission. - name: Playbook to handle IPA permissions
hosts: ipaserver hosts: ipaserver
become: yes become: yes
@@ -56,39 +56,61 @@ Example playbook to make sure permission "MyPermission" is present:
right: all right: all
``` ```
Example playbook to make sure permission "MyPermission" member "privilege" with value "User Administrators" is present:
Example playbook to ensure permission "MyPermission" is present with attr carlicense:
```yaml ```yaml
--- ---
- name: Permission add privilege to a permission - name: Playbook to handle IPA permissions
hosts: ipaserver hosts: ipaserver
become: true become: yes
tasks: tasks:
- name: Ensure permission MyPermission is present with the User Administrators privilege present - name: Ensure permission "MyPermission" is present with attr carlicense
ipapermission: ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: MyPermission name: MyPermission
privilege: "User Administrators" object_type: host
right: all
attrs:
- carlicense
```
Example playbook to ensure attr gecos is present in permission "MyPermission":
```yaml
---
- name: Playbook to handle IPA permissions
hosts: ipaserver
become: yes
tasks:
- name: Ensure attr gecos is present in permission "MyPermission"
ipapermission:
ipaadmin_password: SomeADMINpassword
name: MyPermission
attrs:
- gecos
action: member action: member
``` ```
Example playbook to make sure permission "MyPermission" member "privilege" with value "User Administrators" is absent: Example playbook to ensure attr gecos is absent in permission "MyPermission":
```yaml ```yaml
--- ---
- name: Permission remove privilege from a permission - name: Playbook to handle IPA permissions
hosts: ipaserver hosts: ipaserver
become: true become: yes
tasks: tasks:
- name: Ensure permission MyPermission is present without the User Administrators privilege - name: Ensure attr gecos is present in permission "MyPermission"
ipapermission: ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: MyPermission name: MyPermission
privilege: "User Administrators" attrs:
- gecos
action: member action: member
state: absent state: absent
``` ```
@@ -98,27 +120,30 @@ Example playbook to make sure permission "MyPermission" is absent:
```yaml ```yaml
--- ---
- name: Playbook to manage IPA permission. - name: Playbook to handle IPA permissions
hosts: ipaserver hosts: ipaserver
become: yes become: yes
tasks: tasks:
- ipapermission: - name: Ensure permission "MyPermission" is absent
ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: MyPermission name: MyPermission
state: absent state: absent
``` ```
Example playbook to make sure permission "MyPermission" is renamed to "MyNewPermission": Example playbook to make sure permission "MyPermission" is renamed to "MyNewPermission":
```yaml ```yaml
--- ---
- name: Playbook to manage IPA permission. - name: Playbook to handle IPA permissions
hosts: ipaserver hosts: ipaserver
become: yes become: yes
tasks: tasks:
- ipapermission: - name: Ensure permission "MyPermission" is renamed to "MyNewPermission
ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: MyPermission name: MyPermission
rename: MyNewPermission rename: MyNewPermission
@@ -126,8 +151,6 @@ Example playbook to make sure permission "MyPermission" is renamed to "MyNewPerm
``` ```
Variables Variables
--------- ---------
@@ -140,7 +163,7 @@ Variable | Description | Required
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no `ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
`name` \| `cn` | The permission name string. | yes `name` \| `cn` | The permission name string. | yes
`right` \| `ipapermright` | Rights to grant. It can be a list of one or more of `read`, `search`, `compare`, `write`, `add`, `delete`, and `all` default: `all` | no `right` \| `ipapermright` | Rights to grant. It can be a list of one or more of `read`, `search`, `compare`, `write`, `add`, `delete`, and `all` default: `all` | no
`attrs` | All attributes to which the permission applies | no `attrs` | All attributes to which the permission applies. | no
`bindtype` \| `ipapermbindruletype` | Bind rule type. It can be one of `permission`, `all`, `self`, or `anonymous` defaults to `permission` for new permissions. Bind rule type `self` can only be used on IPA versions 4.8.7 or up.| no `bindtype` \| `ipapermbindruletype` | Bind rule type. It can be one of `permission`, `all`, `self`, or `anonymous` defaults to `permission` for new permissions. Bind rule type `self` can only be used on IPA versions 4.8.7 or up.| no
`subtree` \| `ipapermlocation` | Subtree to apply permissions to | no `subtree` \| `ipapermlocation` | Subtree to apply permissions to | no
`filter` \| `extratargetfilter` | Extra target filter | no `filter` \| `extratargetfilter` | Extra target filter | no
@@ -153,10 +176,12 @@ Variable | Description | Required
`object_type` | Type of IPA object (sets subtree and objectClass targetfilter) | no `object_type` | Type of IPA object (sets subtree and objectClass targetfilter) | no
`no_members` | Suppress processing of membership | no `no_members` | Suppress processing of membership | no
`rename` | Rename the permission object | no `rename` | Rename the permission object | no
`privilege` | Member Privilege of Permission | no
`action` | Work on permission or member level. It can be on of `member` or `permission` and defaults to `permission`. | no `action` | Work on permission or member level. It can be on of `member` or `permission` and defaults to `permission`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, or `renamed` default: `present`. | no `state` | The state to ensure. It can be one of `present`, `absent`, or `renamed` default: `present`. | no
The `includedattrs` and `excludedattrs` variables are only usable for managed permisions and are not exposed by the module. Using `attrs` for managed permissions will result in the automatic generation of `includedattrs` and `excludedattrs` in the IPA server.
Authors Authors
======= =======

View File

@@ -154,7 +154,7 @@ ipaserver_domain=test.local
ipaserver_realm=TEST.LOCAL ipaserver_realm=TEST.LOCAL
``` ```
The admin principle is ```admin``` by default. Please set ```ipaadmin_principal``` if you need to change it. The admin principal is ```admin``` by default. Please set ```ipaadmin_principal``` if you need to change it.
You can also add more setting here, like for example to enable the DNS server or to set auto-forwarders: You can also add more setting here, like for example to enable the DNS server or to set auto-forwarders:
```yaml ```yaml

View File

@@ -28,6 +28,7 @@ import os
import uuid import uuid
import tempfile import tempfile
import shutil import shutil
import netaddr
import gssapi import gssapi
from datetime import datetime from datetime import datetime
from pprint import pformat from pprint import pformat
@@ -413,6 +414,24 @@ def is_valid_port(port):
return False return False
def is_ip_address(ipaddr):
"""Test if given IP address is a valid IPv4 or IPv6 address."""
try:
netaddr.IPAddress(str(ipaddr))
except (netaddr.AddrFormatError, ValueError):
return False
return True
def is_ip_network_address(ipaddr):
"""Test if given IP address is a valid IPv4 or IPv6 address."""
try:
netaddr.IPNetwork(str(ipaddr))
except (netaddr.AddrFormatError, ValueError):
return False
return True
def is_ipv4_addr(ipaddr): def is_ipv4_addr(ipaddr):
"""Test if given IP address is a valid IPv4 address.""" """Test if given IP address is a valid IPv4 address."""
try: try:

View File

@@ -428,7 +428,8 @@ def main():
if params \ if params \
and not compare_args_ipa(ansible_module, params, res_show): and not compare_args_ipa(ansible_module, params, res_show):
changed = True changed = True
api_command_no_name(ansible_module, "config_mod", params) if not ansible_module.check_mode:
api_command_no_name(ansible_module, "config_mod", params)
else: else:
rawresult = api_command_no_name(ansible_module, "config_show", {}) rawresult = api_command_no_name(ansible_module, "config_show", {})

View File

@@ -310,6 +310,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -233,7 +233,8 @@ def main():
# Execute command only if configuration changes. # Execute command only if configuration changes.
if not compare_args_ipa(ansible_module, args, res_find): if not compare_args_ipa(ansible_module, args, res_find):
try: try:
api_command_no_name(ansible_module, 'dnsconfig_mod', args) if not ansible_module.check_mode:
api_command_no_name(ansible_module, 'dnsconfig_mod', args)
# If command did not fail, something changed. # If command did not fail, something changed.
changed = True changed = True

View File

@@ -380,6 +380,12 @@ def main():
[name, 'dnsforwardzone_remove_permission', {}] [name, 'dnsforwardzone_remove_permission', {}]
) )
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0,
**exit_args)
# Execute commands
for name, command, args in commands: for name, command, args in commands:
api_command(ansible_module, command, name, args) api_command(ansible_module, command, name, args)
changed = True changed = True

View File

@@ -1375,10 +1375,9 @@ def define_commands_for_present_state(module, zone_name, entry, res_find):
# remove record from args, as it will not be used again. # remove record from args, as it will not be used again.
del args[record] del args[record]
else: else:
for f in part_fields: _args = {k: args[k] for k in part_fields if k in args}
_args = {k: args[k] for k in part_fields} _args['idnsname'] = name
_args['idnsname'] = name _commands.append([zone_name, 'dnsrecord_add', _args])
_commands.append([zone_name, 'dnsrecord_add', _args])
# clean used fields from args # clean used fields from args
for f in part_fields: for f in part_fields:
if f in args: if f in args:
@@ -1497,6 +1496,10 @@ def main():
if cmds: if cmds:
commands.extend(cmds) commands.extend(cmds)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:
try: try:

View File

@@ -210,9 +210,9 @@ dnszone:
from ipapython.dnsutil import DNSName # noqa: E402 from ipapython.dnsutil import DNSName # noqa: E402
from ansible.module_utils.ansible_freeipa_module import ( from ansible.module_utils.ansible_freeipa_module import (
FreeIPABaseModule, FreeIPABaseModule,
is_ipv4_addr, is_ip_address,
is_ipv6_addr, is_ip_network_address,
is_valid_port, is_valid_port
) # noqa: E402 ) # noqa: E402
import ipalib.errors import ipalib.errors
import netaddr import netaddr
@@ -252,7 +252,13 @@ class DNSZoneModule(FreeIPABaseModule):
def validate_ips(self, ips, error_msg): def validate_ips(self, ips, error_msg):
invalid_ips = [ invalid_ips = [
ip for ip in ips if not is_ipv4_addr(ip) or is_ipv6_addr(ip) ip for ip in ips
if not any([
is_ip_address(ip),
is_ip_network_address(ip),
ip == "any",
ip == "none"
])
] ]
if any(invalid_ips): if any(invalid_ips):
self.fail_json(msg=error_msg % invalid_ips) self.fail_json(msg=error_msg % invalid_ips)
@@ -309,7 +315,7 @@ class DNSZoneModule(FreeIPABaseModule):
forwarders = [] forwarders = []
for forwarder in self.ipa_params.forwarders: for forwarder in self.ipa_params.forwarders:
ip_address = forwarder.get("ip_address") ip_address = forwarder.get("ip_address")
if not (is_ipv4_addr(ip_address) or is_ipv6_addr(ip_address)): if not (is_ip_address(ip_address)):
self.fail_json( self.fail_json(
msg="Invalid IP for DNS forwarder: %s" % ip_address msg="Invalid IP for DNS forwarder: %s" % ip_address
) )

View File

@@ -616,6 +616,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -500,6 +500,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []

View File

@@ -195,6 +195,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -300,6 +300,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []
for name, command, args in commands: for name, command, args in commands:

View File

@@ -1347,6 +1347,10 @@ def main():
del host_set del host_set
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []

View File

@@ -463,6 +463,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:
try: try:

View File

@@ -190,6 +190,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -102,10 +102,6 @@ options:
rename: rename:
description: Rename the permission object description: Rename the permission object
required: false required: false
privilege:
description: Member Privilege of Permission
required: false
type: list
action: action:
description: Work on permission or member privilege level. description: Work on permission or member privilege level.
choices: ["permission", "member"] choices: ["permission", "member"]
@@ -126,19 +122,6 @@ EXAMPLES = """
bindtype: permission bindtype: permission
object_type: host object_type: host
# Ensure permission "NAME" member privilege VALUE is present
- ipapermission:
name: "Add Automember Rebuild Membership Task"
privilege: "Automember Task Administrator"
action: member
# Ensure permission "NAME" member privilege VALUE is absent
- ipapermission:
name: "Add Automember Rebuild Membership Task"
privilege: "IPA Masters Readers"
action: member
state: absent
# Ensure permission NAME is absent # Ensure permission NAME is absent
- ipapermission: - ipapermission:
name: "Removed Permission Name" name: "Removed Permission Name"
@@ -152,8 +135,7 @@ RETURN = """
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_freeipa_module import \ from ansible.module_utils.ansible_freeipa_module import \
temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
compare_args_ipa, module_params_get, gen_add_del_lists, \ compare_args_ipa, module_params_get, api_check_ipa_version
api_check_ipa_version
import six import six
if six.PY3: if six.PY3:
@@ -207,13 +189,6 @@ def gen_args(right, attrs, bindtype, subtree,
return _args return _args
def gen_member_args(privilege):
_args = {}
if privilege is not None:
_args["privilege"] = privilege
return _args
def main(): def main():
ansible_module = AnsibleModule( ansible_module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
@@ -252,7 +227,6 @@ def main():
required=False), required=False),
no_members=dict(type=bool, default=None, require=False), no_members=dict(type=bool, default=None, require=False),
rename=dict(type="str", default=None, required=False), rename=dict(type="str", default=None, required=False),
privilege=dict(type="list", default=None, required=False),
action=dict(type="str", default="permission", action=dict(type="str", default="permission",
choices=["member", "permission"]), choices=["member", "permission"]),
@@ -289,7 +263,6 @@ def main():
object_type = module_params_get(ansible_module, "object_type") object_type = module_params_get(ansible_module, "object_type")
no_members = module_params_get(ansible_module, "no_members") no_members = module_params_get(ansible_module, "no_members")
rename = module_params_get(ansible_module, "rename") rename = module_params_get(ansible_module, "rename")
privilege = module_params_get(ansible_module, "privilege")
action = module_params_get(ansible_module, "action") action = module_params_get(ansible_module, "action")
# state # state
@@ -304,10 +277,12 @@ def main():
ansible_module.fail_json( ansible_module.fail_json(
msg="Only one permission can be added at a time.") msg="Only one permission can be added at a time.")
if action == "member": if action == "member":
invalid = ["right", "attrs", "bindtype", "subtree", invalid = ["right", "bindtype", "subtree",
"extra_target_filter", "rawfilter", "target", "extra_target_filter", "rawfilter", "target",
"targetto", "targetfrom", "memberof", "targetgroup", "targetto", "targetfrom", "memberof", "targetgroup",
"object_type", "rename"] "object_type", "rename"]
else:
invalid = ["rename"]
if state == "renamed": if state == "renamed":
if len(names) != 1: if len(names) != 1:
@@ -315,7 +290,7 @@ def main():
msg="Only one permission can be renamed at a time.") msg="Only one permission can be renamed at a time.")
if action == "member": if action == "member":
ansible_module.fail_json( ansible_module.fail_json(
msg="Member Privileges cannot be renamed") msg="Member action can not be used with state 'renamed'")
invalid = ["right", "attrs", "bindtype", "subtree", invalid = ["right", "attrs", "bindtype", "subtree",
"extra_target_filter", "rawfilter", "target", "targetto", "extra_target_filter", "rawfilter", "target", "targetto",
"targetfrom", "memberof", "targetgroup", "object_type", "targetfrom", "memberof", "targetgroup", "object_type",
@@ -324,12 +299,13 @@ def main():
if state == "absent": if state == "absent":
if len(names) < 1: if len(names) < 1:
ansible_module.fail_json(msg="No name given.") ansible_module.fail_json(msg="No name given.")
invalid = ["right", "attrs", "bindtype", "subtree", invalid = ["right",
"bindtype", "subtree",
"extra_target_filter", "rawfilter", "target", "targetto", "extra_target_filter", "rawfilter", "target", "targetto",
"targetfrom", "memberof", "targetgroup", "object_type", "targetfrom", "memberof", "targetgroup", "object_type",
"no_members", "rename"] "no_members", "rename"]
if action == "permission": if action != "member":
invalid.append("privilege") invalid += ["attrs"]
for x in invalid: for x in invalid:
if vars()[x] is not None: if vars()[x] is not None:
@@ -366,11 +342,6 @@ def main():
targetto, targetfrom, memberof, targetgroup, targetto, targetfrom, memberof, targetgroup,
object_type, no_members, rename) object_type, no_members, rename)
no_members_value = False
if no_members is not None:
no_members_value = no_members
if action == "permission": if action == "permission":
# Found the permission # Found the permission
if res_find is not None: if res_find is not None:
@@ -383,41 +354,18 @@ def main():
else: else:
commands.append([name, "permission_add", args]) commands.append([name, "permission_add", args])
member_args = gen_member_args(privilege)
if not compare_args_ipa(ansible_module, member_args,
res_find):
# Generate addition and removal lists
privilege_add, privilege_del = gen_add_del_lists(
privilege, res_find.get("member_privilege"))
# Add members
if len(privilege_add) > 0:
commands.append([name, "permission_add_member",
{
"privilege": privilege_add,
"no_members": no_members_value
}])
# Remove members
if len(privilege_del) > 0:
commands.append([name, "permission_remove_member",
{
"privilege": privilege_del,
"no_members": no_members_value
}])
elif action == "member": elif action == "member":
if res_find is None: if res_find is None:
ansible_module.fail_json( ansible_module.fail_json(
msg="No permission '%s'" % name) msg="No permission '%s'" % name)
if privilege is None: # attrs
ansible_module.fail_json(msg="No privilege given") if attrs is not None:
_attrs = list(set(list(res_find["attrs"]) + attrs))
if len(_attrs) > len(res_find["attrs"]):
commands.append([name, "permission_mod",
{"attrs": _attrs}])
commands.append([name, "permission_add_member",
{
"privilege": privilege,
"no_members": no_members_value
}])
else: else:
ansible_module.fail_json( ansible_module.fail_json(
msg="Unknown action '%s'" % action) msg="Unknown action '%s'" % action)
@@ -455,17 +403,28 @@ def main():
ansible_module.fail_json( ansible_module.fail_json(
msg="No permission '%s'" % name) msg="No permission '%s'" % name)
if privilege is None: # attrs
ansible_module.fail_json(msg="No privilege given") if attrs is not None:
# New attribute list (remove given ones from find
# result)
# Make list with unique entries
_attrs = list(set(res_find["attrs"]) - set(attrs))
if len(_attrs) < 1:
ansible_module.fail_json(
msg="At minimum one attribute is needed.")
commands.append([name, "permission_remove_member", # Entries New number of attributes is smaller
{ if len(_attrs) < len(res_find["attrs"]):
"privilege": privilege, commands.append([name, "permission_mod",
}]) {"attrs": _attrs}])
else: else:
ansible_module.fail_json(msg="Unknown state '%s'" % state) ansible_module.fail_json(msg="Unknown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -312,6 +312,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -284,6 +284,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -257,7 +257,7 @@ def filter_service(module, res_find, predicate):
return _services return _services
def ensure_role_with_members_is_present(module, name, res_find): def ensure_role_with_members_is_present(module, name, res_find, action):
"""Define commands to ensure member are present for action `role`.""" """Define commands to ensure member are present for action `role`."""
commands = [] commands = []
privilege_add, privilege_del = gen_add_del_lists( privilege_add, privilege_del = gen_add_del_lists(
@@ -267,7 +267,7 @@ def ensure_role_with_members_is_present(module, name, res_find):
if privilege_add: if privilege_add:
commands.append([name, "role_add_privilege", commands.append([name, "role_add_privilege",
{"privilege": privilege_add}]) {"privilege": privilege_add}])
if privilege_del: if action == "role" and privilege_del:
commands.append([name, "role_remove_privilege", commands.append([name, "role_remove_privilege",
{"privilege": privilege_del}]) {"privilege": privilege_del}])
@@ -297,7 +297,8 @@ def ensure_role_with_members_is_present(module, name, res_find):
if add_members: if add_members:
commands.append([name, "role_add_member", add_members]) commands.append([name, "role_add_member", add_members])
if del_members: # Only remove members if ensuring role, not acting on members.
if action == "role" and del_members:
commands.append([name, "role_remove_member", del_members]) commands.append([name, "role_remove_member", del_members])
return commands return commands
@@ -355,6 +356,11 @@ def process_commands(module, commands):
errors = [] errors = []
exit_args = {} exit_args = {}
changed = False changed = False
# Check mode exit
if module.check_mode:
return len(commands) > 0, exit_args
for name, command, args in commands: for name, command, args in commands:
try: try:
result = api_command(module, command, name, args) result = api_command(module, command, name, args)
@@ -400,7 +406,9 @@ def role_commands_for_name(module, state, action, name):
if res_find is None: if res_find is None:
module.fail_json(msg="No role '%s'" % name) module.fail_json(msg="No role '%s'" % name)
cmds = ensure_role_with_members_is_present(module, name, res_find) cmds = ensure_role_with_members_is_present(
module, name, res_find, action
)
commands.extend(cmds) commands.extend(cmds)
if state == "absent" and res_find is not None: if state == "absent" and res_find is not None:

View File

@@ -293,6 +293,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -824,6 +824,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []
for name, command, args in commands: for name, command, args in commands:

View File

@@ -182,6 +182,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:
try: try:

View File

@@ -298,6 +298,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:
try: try:

View File

@@ -686,6 +686,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []

View File

@@ -326,6 +326,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute command # Execute command
for command, args, _suffix in commands: for command, args, _suffix in commands:

View File

@@ -244,7 +244,8 @@ def main():
if state == "absent": if state == "absent":
if res_find is not None: if res_find is not None:
del_trust(ansible_module, realm) if not ansible_module.check_mode:
del_trust(ansible_module, realm)
changed = True changed = True
elif res_find is None: elif res_find is None:
if admin is None and trust_secret is None: if admin is None and trust_secret is None:
@@ -256,7 +257,8 @@ def main():
trust_secret, base_id, range_size, range_type, trust_secret, base_id, range_size, range_type,
two_way, external) two_way, external)
add_trust(ansible_module, realm, args) if not ansible_module.check_mode:
add_trust(ansible_module, realm, args)
changed = True changed = True
except Exception as e: except Exception as e:

View File

@@ -1377,6 +1377,10 @@ def main():
del user_set del user_set
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []

View File

@@ -317,10 +317,11 @@ vault:
import os import os
from base64 import b64decode from base64 import b64decode
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
temp_kdestroy, valid_creds, api_connect, api_command, \ temp_kdestroy, valid_creds, api_connect, api_command, \
gen_add_del_lists, compare_args_ipa, module_params_get, exit_raw_json gen_add_del_lists, compare_args_ipa, module_params_get, exit_raw_json
from ipalib.errors import EmptyModlist from ipalib.errors import EmptyModlist, NotFound
def find_vault(module, name, username, service, shared): def find_vault(module, name, username, service, shared):
@@ -351,7 +352,9 @@ def gen_args(description, username, service, shared, vault_type, salt,
password, password_file, public_key, public_key_file, vault_data, password, password_file, public_key, public_key_file, vault_data,
datafile_in, datafile_out): datafile_in, datafile_out):
_args = {} _args = {}
vault_type = vault_type or to_text("symmetric")
_args['ipavaulttype'] = vault_type
if description is not None: if description is not None:
_args['description'] = description _args['description'] = description
if username is not None: if username is not None:
@@ -360,27 +363,32 @@ def gen_args(description, username, service, shared, vault_type, salt,
_args['service'] = service _args['service'] = service
if shared is not None: if shared is not None:
_args['shared'] = shared _args['shared'] = shared
if vault_type is not None:
_args['ipavaulttype'] = vault_type if vault_type == "symmetric":
if salt is not None: if salt is not None:
_args['ipavaultsalt'] = salt _args['ipavaultsalt'] = salt
if public_key is not None: _args['ipavaultpublickey'] = None
_args['ipavaultpublickey'] = b64decode(public_key.encode('utf-8'))
if public_key_file is not None: elif vault_type == "asymmetric":
with open(public_key_file, 'r') as keyfile: if public_key is not None:
keydata = keyfile.read() _args['ipavaultpublickey'] = b64decode(public_key.encode('utf-8'))
_args['ipavaultpublickey'] = keydata.strip().encode('utf-8') if public_key_file is not None:
with open(public_key_file, 'r') as keyfile:
keydata = keyfile.read()
_args['ipavaultpublickey'] = keydata.strip().encode('utf-8')
_args['ipavaultsalt'] = None
elif vault_type == "standard":
_args['ipavaultsalt'] = None
_args['ipavaultpublickey'] = None
return _args return _args
def gen_member_args(args, users, groups, services): def gen_member_args(args, users, groups, services):
_args = args.copy() remove = ['ipavaulttype', 'description', 'ipavaultpublickey',
'ipavaultsalt']
for arg in ['ipavaulttype', 'description', 'ipavaultpublickey', _args = {k: v for k, v in args.items() if k not in remove}
'ipavaultsalt']:
if arg in _args:
del _args[arg]
if any([users, groups, services]): if any([users, groups, services]):
if users is not None: if users is not None:
@@ -395,9 +403,12 @@ def gen_member_args(args, users, groups, services):
return None return None
def data_storage_args(args, data, password, password_file, private_key, def data_storage_args(vault_type, args, data, password, password_file,
private_key_file, datafile_in, datafile_out): private_key, private_key_file, datafile_in,
_args = {} datafile_out):
remove = ['ipavaulttype', 'description', 'ipavaultpublickey',
'ipavaultsalt']
_args = {k: v for k, v in args.items() if k not in remove}
if 'username' in args: if 'username' in args:
_args['username'] = args['username'] _args['username'] = args['username']
@@ -406,15 +417,17 @@ def data_storage_args(args, data, password, password_file, private_key,
if 'shared' in args: if 'shared' in args:
_args['shared'] = args['shared'] _args['shared'] = args['shared']
if password is not None: if vault_type is None or vault_type == "symmetric":
_args['password'] = password if password is not None:
if password_file is not None: _args['password'] = password
_args['password_file'] = password_file if password_file is not None:
_args['password_file'] = password_file
if private_key is not None: if vault_type == "asymmetric":
_args['private_key'] = private_key if private_key is not None:
if private_key_file is not None: _args['private_key'] = private_key
_args['private_key_file'] = private_key_file if private_key_file is not None:
_args['private_key_file'] = private_key_file
if datafile_in is not None: if datafile_in is not None:
_args['in'] = datafile_in _args['in'] = datafile_in
@@ -427,9 +440,6 @@ def data_storage_args(args, data, password, password_file, private_key,
if datafile_out is not None: if datafile_out is not None:
_args['out'] = datafile_out _args['out'] = datafile_out
if private_key_file is not None:
_args['private_key_file'] = private_key_file
return _args return _args
@@ -441,7 +451,7 @@ def check_parameters(module, state, action, description, username, service,
new_password, new_password_file): new_password, new_password_file):
invalid = [] invalid = []
if state == "present": if state == "present":
invalid = ['private_key', 'private_key_file', 'datafile_out'] invalid = ['datafile_out']
if all([password, password_file]) \ if all([password, password_file]) \
or all([new_password, new_password_file]): or all([new_password, new_password_file]):
@@ -454,7 +464,7 @@ def check_parameters(module, state, action, description, username, service,
"change symmetric vault password.") "change symmetric vault password.")
if action == "member": if action == "member":
invalid.extend(['description']) invalid.extend(['description', 'vault_type'])
elif state == "absent": elif state == "absent":
invalid = ['description', 'salt', 'vault_type', 'private_key', invalid = ['description', 'salt', 'vault_type', 'private_key',
@@ -480,12 +490,6 @@ def check_parameters(module, state, action, description, username, service,
msg="Argument '%s' can not be used with state '%s', " msg="Argument '%s' can not be used with state '%s', "
"action '%s'" % (arg, state, action)) "action '%s'" % (arg, state, action))
for arg in invalid:
if vars()[arg] is not None:
module.fail_json(
msg="Argument '%s' can not be used with state '%s', "
"action '%s'" % (arg, state, action))
def check_encryption_params(module, state, action, vault_type, salt, def check_encryption_params(module, state, action, vault_type, salt,
password, password_file, public_key, password, password_file, public_key,
@@ -494,6 +498,10 @@ def check_encryption_params(module, state, action, vault_type, salt,
new_password, new_password_file, res_find): new_password, new_password_file, res_find):
vault_type_invalid = [] vault_type_invalid = []
existing_type = None
if res_find:
existing_type = res_find["ipavaulttype"][0]
if vault_type is None and res_find is not None: if vault_type is None and res_find is not None:
vault_type = res_find['ipavaulttype'] vault_type = res_find['ipavaulttype']
if isinstance(vault_type, (tuple, list)): if isinstance(vault_type, (tuple, list)):
@@ -536,47 +544,45 @@ def check_encryption_params(module, state, action, vault_type, salt,
msg="Assymmetric vault requires public_key " msg="Assymmetric vault requires public_key "
"or public_key_file to store data.") "or public_key_file to store data.")
for param in vault_type_invalid: valid_fields = []
if existing_type == "symmetric":
valid_fields = [
'password', 'password_file', 'new_password', 'new_password_file',
'salt'
]
if existing_type == "asymmetric":
valid_fields = [
'public_key', 'public_key_file', 'private_key', 'private_key_file'
]
check_fields = [f for f in vault_type_invalid if f not in valid_fields]
for param in check_fields:
if vars()[param] is not None: if vars()[param] is not None:
module.fail_json( module.fail_json(
msg="Argument '%s' cannot be used with vault type '%s'" % msg="Argument '%s' cannot be used with vault type '%s'" %
(param, vault_type or 'symmetric')) (param, vault_type or 'symmetric'))
def change_password(module, res_find, password, password_file, new_password, def get_stored_data(module, res_find, args):
new_password_file): """Retrieve data stored in the vault."""
"""
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. # prepare arguments to retrieve data.
name = res_find["cn"][0] name = res_find["cn"][0]
args = {} copy_args = []
if password: if res_find['ipavaulttype'][0] == "symmetric":
args["password"] = password copy_args = ["password", "password_file"]
if password_file: if res_find['ipavaulttype'][0] == "asymmetric":
args["password_file"] = password_file copy_args = ["private_key", "private_key_file"]
# retrieve current stored data
result = api_command(module, 'vault_retrieve', name, args)
# modify arguments to store data with new password. pwdargs = {arg: args[arg] for arg in copy_args if arg in args}
args = {"override_password": True, "data": result['result']['data']}
if new_password: # retrieve vault stored data
args["password"] = new_password try:
if new_password_file: result = api_command(module, 'vault_retrieve', name, pwdargs)
args["password_file"] = new_password_file except NotFound:
# return the command to store data with the new password. return None
return [(name, "vault_archive", args)]
return result['result'].get('data')
def main(): def main():
@@ -594,10 +600,12 @@ def main():
default=None, required=False, default=None, required=False,
choices=["standard", "symmetric", "asymmetric"]), choices=["standard", "symmetric", "asymmetric"]),
vault_public_key=dict(type="str", required=False, default=None, vault_public_key=dict(type="str", required=False, default=None,
aliases=['ipavaultpublickey', 'public_key']), aliases=['ipavaultpublickey', 'public_key',
'new_public_key']),
vault_public_key_file=dict(type="str", required=False, vault_public_key_file=dict(type="str", required=False,
default=None, default=None,
aliases=['public_key_file']), aliases=['public_key_file',
'new_public_key_file']),
vault_private_key=dict( vault_private_key=dict(
type="str", required=False, default=None, no_log=True, type="str", required=False, default=None, no_log=True,
aliases=['ipavaultprivatekey', 'private_key']), aliases=['ipavaultprivatekey', 'private_key']),
@@ -742,6 +750,11 @@ def main():
res_find = find_vault( res_find = find_vault(
ansible_module, name, username, service, shared) ansible_module, name, username, service, shared)
# Set default vault_type if needed.
res_type = res_find.get('ipavaulttype')[0] if res_find else None
if vault_type is None:
vault_type = res_type if res_find is not None else u"symmetric"
# Generate args # Generate args
args = gen_args(description, username, service, shared, vault_type, args = gen_args(description, username, service, shared, vault_type,
salt, password, password_file, public_key, salt, password, password_file, public_key,
@@ -749,14 +762,6 @@ def main():
datafile_out) datafile_out)
pwdargs = None pwdargs = None
# Set default vault_type if needed.
if vault_type is None and vault_data is not None:
if res_find is not None:
res_vault_type = res_find.get('ipavaulttype')[0]
args['ipavaulttype'] = vault_type = res_vault_type
else:
args['ipavaulttype'] = vault_type = u"symmetric"
# Create command # Create command
if state == "present": if state == "present":
# verify data encription args # verify data encription args
@@ -766,16 +771,52 @@ def main():
private_key_file, vault_data, datafile_in, datafile_out, private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file, res_find) new_password, new_password_file, res_find)
# Found the vault change_passwd = any([
new_password, new_password_file,
(private_key or private_key_file) and
(public_key or public_key_file)
])
if action == "vault": if action == "vault":
# Found the vault
if res_find is not None: if res_find is not None:
# For all settings is args, check if there are arg_type = args.get("ipavaulttype")
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([name, "vault_mod_internal", args])
modified = not compare_args_ipa(ansible_module,
args, res_find)
if arg_type != res_type or change_passwd:
stargs = data_storage_args(
res_type, args, vault_data, password,
password_file, private_key,
private_key_file, datafile_in,
datafile_out)
stored = get_stored_data(
ansible_module, res_find, stargs
)
if stored:
vault_data = \
(stored or b"").decode("utf-8")
remove_attrs = {
"symmetric": ["private_key", "public_key"],
"asymmetric": ["password", "ipavaultsalt"],
"standard": [
"private_key", "public_key",
"password", "ipavaultsalt"
],
}
for attr in remove_attrs.get(arg_type, []):
if attr in args:
del args[attr]
if vault_type == 'symmetric':
if 'ipavaultsalt' not in args:
args['ipavaultsalt'] = os.urandom(32)
else:
args['ipavaultsalt'] = b''
if modified:
commands.append([name, "vault_mod_internal", args])
else: else:
if vault_type == 'symmetric' \ if vault_type == 'symmetric' \
and 'ipavaultsalt' not in args: and 'ipavaultsalt' not in args:
@@ -851,16 +892,22 @@ def main():
ownerservices) ownerservices)
commands.append([name, 'vault_add_owner', owner_args]) commands.append([name, 'vault_add_owner', owner_args])
pwdargs = data_storage_args(
args, vault_data, password, password_file, private_key,
private_key_file, datafile_in, datafile_out)
if any([vault_data, datafile_in]): if any([vault_data, datafile_in]):
commands.append([name, "vault_archive", pwdargs]) if change_passwd:
pwdargs = data_storage_args(
vault_type, args, vault_data, new_password,
new_password_file, private_key, private_key_file,
datafile_in, datafile_out)
else:
pwdargs = data_storage_args(
vault_type, args, vault_data, password,
password_file, private_key, private_key_file,
datafile_in, datafile_out)
cmds = change_password( pwdargs['override_password'] = True
ansible_module, res_find, password, password_file, pwdargs.pop("private_key", None)
new_password, new_password_file) pwdargs.pop("private_key_file", None)
commands.extend(cmds) commands.append([name, "vault_archive", pwdargs])
elif state == "retrieved": elif state == "retrieved":
if res_find is None: if res_find is None:
@@ -875,8 +922,9 @@ def main():
new_password, new_password_file, res_find) new_password, new_password_file, res_find)
pwdargs = data_storage_args( pwdargs = data_storage_args(
args, vault_data, password, password_file, private_key, res_find["ipavaulttype"][0], args, vault_data, password,
private_key_file, datafile_in, datafile_out) password_file, private_key, private_key_file, datafile_in,
datafile_out)
if 'data' in pwdargs: if 'data' in pwdargs:
del pwdargs['data'] del pwdargs['data']
@@ -888,6 +936,10 @@ def main():
if action == "vault": if action == "vault":
if res_find is not None: if res_find is not None:
remove = ['ipavaultsalt', 'ipavaultpublickey']
args = {
k: v for k, v in args.items() if k not in remove
}
commands.append([name, "vault_del", args]) commands.append([name, "vault_del", args])
elif action == "member": elif action == "member":
@@ -910,6 +962,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unknown state '%s'" % state) ansible_module.fail_json(msg="Unknown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
errors = [] errors = []

View File

@@ -1,3 +1,4 @@
-r requirements-tests.txt -r requirements-tests.txt
ipdb ipdb
pre-commit pre-commit
flake8-bugbear

View File

@@ -2,6 +2,6 @@
pytest>=2.7 pytest>=2.7
pytest-sourceorder>=0.5 pytest-sourceorder>=0.5
pytest-split-tests>=1.0.3 pytest-split-tests>=1.0.3
testinfra>=5.0 pytest-testinfra>=5.0
jmespath>=0.9 # needed for the `json_query` filter jmespath>=0.9 # needed for the `json_query` filter
pyyaml>=3 pyyaml>=3

View File

@@ -4,13 +4,13 @@
- name: Create backup - name: Create backup
shell: > shell: >
ipa-backup ipa-backup
{{ "--gpg" if ipabackup_gpg | bool }} {{ "--gpg" if ipabackup_gpg | bool else "" }}
{{ "--gpg-keyring="+ipabackup_gpg_keyring if ipabackup_gpg_keyring is defined }} {{ "--gpg-keyring="+ipabackup_gpg_keyring if ipabackup_gpg_keyring is defined else "" }}
{{ "--data" if ipabackup_data | bool }} {{ "--data" if ipabackup_data | bool else "" }}
{{ "--logs" if ipabackup_logs | bool }} {{ "--logs" if ipabackup_logs | bool else "" }}
{{ "--online" if ipabackup_online | bool }} {{ "--online" if ipabackup_online | bool else "" }}
{{ "--disable-role-check" if ipabackup_disable_role_check | bool }} {{ "--disable-role-check" if ipabackup_disable_role_check | bool else "" }}
{{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined }} {{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined else "" }}
register: result_ipabackup register: result_ipabackup
- block: - block:

View File

@@ -105,13 +105,13 @@
ipa-restore ipa-restore
{{ ipabackup_item }} {{ ipabackup_item }}
--unattended --unattended
{{ "--password="+ipabackup_password if ipabackup_password is defined }} {{ "--password="+ipabackup_password if ipabackup_password is defined else "" }}
{{ "--data" if ipabackup_data | bool }} {{ "--data" if ipabackup_data | bool else "" }}
{{ "--online" if ipabackup_online | bool }} {{ "--online" if ipabackup_online | bool else "" }}
{{ "--instance="+ipabackup_instance if ipabackup_instance is defined }} {{ "--instance="+ipabackup_instance if ipabackup_instance is defined else "" }}
{{ "--backend="+ipabackup_backend if ipabackup_backend is defined }} {{ "--backend="+ipabackup_backend if ipabackup_backend is defined else "" }}
{{ "--no-logs" if ipabackup_no_logs | bool }} {{ "--no-logs" if ipabackup_no_logs | bool else "" }}
{{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined }} {{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined else "" }}
register: result_iparestore register: result_iparestore
ignore_errors: yes ignore_errors: yes
@@ -127,21 +127,21 @@
command: > command: >
firewall-cmd firewall-cmd
--permanent --permanent
--zone="{{ ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined }}" {{ "--zone="+ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
--add-service=freeipa-ldap --add-service=freeipa-ldap
--add-service=freeipa-ldaps --add-service=freeipa-ldaps
{{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services }} {{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services else "" }}
{{ "--add-service=dns" if ipabackup_service_dns in ipabackup_services }} {{ "--add-service=dns" if ipabackup_service_dns in ipabackup_services else "" }}
{{ "--add-service=ntp" if ipabackup_service_ntp in ipabackup_services }} {{ "--add-service=ntp" if ipabackup_service_ntp in ipabackup_services else "" }}
when: ipabackup_setup_firewalld | bool when: ipabackup_setup_firewalld | bool
- name: Configure firewalld runtime - name: Configure firewalld runtime
command: > command: >
firewall-cmd firewall-cmd
--zone="{{ ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined }}" {{ "--zone="+ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
--add-service=freeipa-ldap --add-service=freeipa-ldap
--add-service=freeipa-ldaps --add-service=freeipa-ldaps
{{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services }} {{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services else "" }}
{{ "--add-service=dns" if ipabackup_service_dns in ipabackup_services }} {{ "--add-service=dns" if ipabackup_service_dns in ipabackup_services else "" }}
{{ "--add-service=ntp" if ipabackup_service_ntp in ipabackup_services }} {{ "--add-service=ntp" if ipabackup_service_ntp in ipabackup_services else "" }}
when: ipabackup_setup_firewalld | bool when: ipabackup_setup_firewalld | bool

View File

@@ -325,8 +325,6 @@ def main():
'external_cert_files') 'external_cert_files')
# options.subject_base = ansible_module.params.get('subject_base') # options.subject_base = ansible_module.params.get('subject_base')
# options.ca_subject = ansible_module.params.get('ca_subject') # options.ca_subject = ansible_module.params.get('ca_subject')
options.no_dnssec_validation = ansible_module.params.get(
'no_dnssec_validation')
# dns # dns
options.allow_zone_overlap = ansible_module.params.get( options.allow_zone_overlap = ansible_module.params.get(
'allow_zone_overlap') 'allow_zone_overlap')
@@ -338,7 +336,7 @@ def main():
options.auto_forwarders = ansible_module.params.get('auto_forwarders') options.auto_forwarders = ansible_module.params.get('auto_forwarders')
options.forward_policy = ansible_module.params.get('forward_policy') options.forward_policy = ansible_module.params.get('forward_policy')
options.no_dnssec_validation = ansible_module.params.get( options.no_dnssec_validation = ansible_module.params.get(
'no_dnssec_validationdnssec_validation') 'no_dnssec_validation')
# ad trust # ad trust
options.enable_compat = ansible_module.params.get('enable_compat') options.enable_compat = ansible_module.params.get('enable_compat')
options.netbios_name = ansible_module.params.get('netbios_name') options.netbios_name = ansible_module.params.get('netbios_name')

View File

@@ -143,7 +143,7 @@ def main():
options.forwarders = ansible_module.params.get('forwarders') options.forwarders = ansible_module.params.get('forwarders')
options.forward_policy = ansible_module.params.get('forward_policy') options.forward_policy = ansible_module.params.get('forward_policy')
options.no_dnssec_validation = ansible_module.params.get( options.no_dnssec_validation = ansible_module.params.get(
'no_dnssec_validationdnssec_validation') 'no_dnssec_validation')
# additional # additional
dns.ip_addresses = ansible_module_get_parsed_ip_addresses( dns.ip_addresses = ansible_module_get_parsed_ip_addresses(
ansible_module, 'dns_ip_addresses') ansible_module, 'dns_ip_addresses')

View File

@@ -37,41 +37,127 @@
register: result register: result
failed_when: result.changed or result.failed failed_when: result.changed or result.failed
- name: Ensure permission perm-test-1 member User Administrators privilege is present - name: Ensure permission perm-test-1 is present with attr carlicense
ipapermission: ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: perm-test-1 name: perm-test-1
privilege: "User Administrators" attrs:
- carlicense
register: result
failed_when: not result.changed or result.failed
- name: Ensure permission perm-test-1 is present with attr carlicense again
ipapermission:
ipaadmin_password: SomeADMINpassword
name: perm-test-1
attrs:
- carlicense
register: result
failed_when: result.changed or result.failed
- name: Ensure permission perm-test-1 is present with attr carlicense and displayname
ipapermission:
ipaadmin_password: SomeADMINpassword
name: perm-test-1
attrs:
- carlicense
- displayname
register: result
failed_when: not result.changed or result.failed
- name: Ensure permission perm-test-1 is present with attr carlicense and displayname again
ipapermission:
ipaadmin_password: SomeADMINpassword
name: perm-test-1
attrs:
- carlicense
- displayname
register: result
failed_when: result.changed or result.failed
- name: Ensure attr gecos is present in permission perm-test-1
ipapermission:
ipaadmin_password: SomeADMINpassword
name: perm-test-1
attrs:
- gecos
action: member action: member
register: result register: result
failed_when: not result.changed or result.failed failed_when: not result.changed or result.failed
- name: Ensure permission perm-test-1 member User Administrators privilege is present again - name: Ensure attr gecos is present in permission perm-test-1 again
ipapermission: ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: perm-test-1 name: perm-test-1
privilege: "User Administrators" attrs:
- gecos
action: member action: member
register: result register: result
failed_when: result.changed or result.failed failed_when: result.changed or result.failed
- name: Ensure permission perm-test-1 member User Administrators privilege is absent - name: Ensure attr gecos is absent in permission perm-test-1
ipapermission: ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: perm-test-1 name: perm-test-1
privilege: "User Administrators" attrs:
- gecos
action: member action: member
state: absent state: absent
register: result register: result
failed_when: not result.changed or result.failed failed_when: not result.changed or result.failed
# NOTE: We use the "User Administrators" Privilege here since we don't have a module - name: Ensure attr gecos is absent in permission perm-test-1 again
# to make one. A test privilege should be used in the future.
- name: Ensure permission perm-test-1 member User Administrators privilege is absent again
ipapermission: ipapermission:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: perm-test-1 name: perm-test-1
privilege: "User Administrators" attrs:
- gecos
action: member
state: absent
register: result
failed_when: result.changed or result.failed
- name: Ensure attributes carlicense and displayname are present in permission "System{{':'}} Update DNS Entries"
ipapermission:
ipaadmin_password: SomeADMINpassword
name: "System: Update DNS Entries"
attrs:
- carlicense
- displayname
action: member
register: result
failed_when: not result.changed or result.failed
- name: Ensure attributes carlicense and displayname are present in permission "System{{':'}} Update DNS Entries" again
ipapermission:
ipaadmin_password: SomeADMINpassword
name: "System: Update DNS Entries"
attrs:
- carlicense
- displayname
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure attributes carlicense and displayname are present in permission "System{{':'}} Update DNS Entries"
ipapermission:
ipaadmin_password: SomeADMINpassword
name: "System: Update DNS Entries"
attrs:
- carlicense
- displayname
action: member
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Ensure attributes carlicense and displayname are present in permission "System{{':'}} Update DNS Entries" again
ipapermission:
ipaadmin_password: SomeADMINpassword
name: "System: Update DNS Entries"
attrs:
- carlicense
- displayname
action: member action: member
state: absent state: absent
register: result register: result

View File

@@ -2,31 +2,42 @@
- name: Ensure test user is absent. - name: Ensure test user is absent.
ipauser: ipauser:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: user01 name:
- user01
- user02
- user03
state: absent state: absent
- name: Ensure test group is absent. - name: Ensure test group is absent.
ipagroup: ipagroup:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: group01 name:
- group01
- group02
state: absent state: absent
- name: Ensure test hostgroup is absent. - name: Ensure test hostgroup is absent.
ipahostgroup: ipahostgroup:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: hostgroup01 name:
- hostgroup01
- hostgroup02
state: absent state: absent
- name: Ensure test host is absent. - name: Ensure test host is absent.
ipahost: ipahost:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: "{{ host1_fqdn }}" name:
- "{{ host1_fqdn }}"
- "{{ host2_fqdn }}"
state: absent state: absent
- name: Ensure test service is absent. - name: Ensure test service is absent.
ipaservice: ipaservice:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: "service01/{{ host1_fqdn }}" name:
- "service01/{{ host1_fqdn }}"
- "service02/{{ host2_fqdn }}"
state: absent state: absent
- name: Ensure test roles are absent. - name: Ensure test roles are absent.

View File

@@ -1,7 +1,7 @@
--- ---
- name: Get Domain from server name - name: Get Domain from server name
set_fact: set_fact:
ipaserver_domain: "{{ ansible_fqdn | join ('.') }}" ipaserver_domain: "{{ ansible_fqdn.split('.')[1:] | join ('.') }}"
when: ipaserver_domain is not defined when: ipaserver_domain is not defined
- name: Set fact for realm name - name: Set fact for realm name
@@ -12,3 +12,4 @@
- name: Create FQDN for host01 - name: Create FQDN for host01
set_fact: set_fact:
host1_fqdn: "host01.{{ ipaserver_domain }}" host1_fqdn: "host01.{{ ipaserver_domain }}"
host2_fqdn: "host02.{{ ipaserver_domain }}"

View File

@@ -5,30 +5,49 @@
- name: Ensure test user is present. - name: Ensure test user is present.
ipauser: ipauser:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: user01 users:
first: First - name: user01
last: Last first: First
last: Last
- name: user02
first: First
last: Last
- name: user03
first: First
last: Last
- name: Ensure test group is present. - name: Ensure test group is present.
ipagroup: ipagroup:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: group01 name: "{{ item }}"
with_items:
- group01
- group02
- name: Ensure test host is present. - name: Ensure test host is present.
ipahost: ipahost:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: "{{ host1_fqdn }}" name: "{{ item }}"
force: yes force: yes
with_items:
- "{{ host1_fqdn }}"
- "{{ host2_fqdn }}"
- name: Ensure test hostgroup is present. - name: Ensure test hostgroup is present.
ipahostgroup: ipahostgroup:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: hostgroup01 name: "{{ item[0] }}"
host: host:
- "{{ host1_fqdn }}" - "{{ item[1] }}"
with_nested:
- [hostgroup01, hostgroup02]
- ["{{ host1_fqdn }}", "{{ host2_fqdn }}"]
- name: Ensure test service is present. - name: Ensure test service is present.
ipaservice: ipaservice:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: "service01/{{ host1_fqdn }}" name: "{{ item }}"
force: yes force: yes
with_items:
- "service01/{{ host1_fqdn }}"
- "service02/{{ host2_fqdn }}"

View File

@@ -0,0 +1,259 @@
---
- name: Test service member in role module.
hosts: ipaserver
become: yes
gather_facts: yes
tasks:
- name: Set environment facts.
import_tasks: env_facts.yml
- name: Setup environment.
import_tasks: env_setup.yml
- name: Add role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user: user01
group: group01
hostgroup: hostgroup01
host: "{{ host1_fqdn }}"
service: "service01/{{ host1_fqdn }}"
privilege:
- Automember Readers
- ADTrust Agents
register: result
failed_when: result.failed or not result.changed
# Test fix for https://github.com/freeipa/ansible-freeipa/issues/409
- name: Add new privileges to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
- DNS Administrators
- Group Administrators
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role privileges.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
"Automember Readers" in result.stdout
and "ADTrust Agents" in result.stdout
and "DNS Servers" in result.stdout
and "Host Administrators" in result.stdout
and "DNS Administrators" in result.stdout
and "Group Administrators" in result.stdout
)
vars:
KRB5CCNAME: verify_issue_409
# End of test fix for https://github.com/freeipa/ansible-freeipa/issues/409
# Test fix for https://github.com/freeipa/ansible-freeipa/issues/412
- name: Add new user to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user: user02
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role users.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
"user01" in result.stdout
and "user02" in result.stdout
)
vars:
KRB5CCNAME: verify_issue_412
- name: Add new group to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
group: group02
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role group.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
"group01" in result.stdout
and "group02" in result.stdout
)
vars:
KRB5CCNAME: verify_issue_412
- name: Add new host to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
host: "{{ host2_fqdn }}"
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role hosts.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
host1 in result.stdout
and host2 in result.stdout
)
vars:
KRB5CCNAME: verify_issue_412
host1: " {{ host1_fqdn }}"
host2: " {{ host2_fqdn }}"
- name: Add new hostgroup to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
hostgroup: hostgroup02
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role hostgroups.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
" hostgroup01" in result.stdout
and " hostgroup02" in result.stdout
)
vars:
KRB5CCNAME: verify_issue_412
- name: Add new service to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service: "service02/{{ host2_fqdn }}"
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role services.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
service1 in result.stdout
and service1 in result.stdout
)
vars:
KRB5CCNAME: verify_issue_412
service1: "service01/{{ host1_fqdn }}"
service2: "service02/{{ host2_fqdn }}"
# End of test fix for https://github.com/freeipa/ansible-freeipa/issues/412
# Test fix for https://github.com/freeipa/ansible-freeipa/issues/413
- name: Add new user to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user: user03
action: member
register: result
failed_when: result.failed or not result.changed
- name: Verify role services.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
service1 in result.stdout
and service1 in result.stdout
and "user03" in result.stdout
)
vars:
KRB5CCNAME: verify_issue_413
service1: "service01/{{ host1_fqdn }}"
service2: "service02/{{ host2_fqdn }}"
- name: Remove user from role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user: user03
action: member
state: absent
register: result
failed_when: result.failed or not result.changed
- name: Verify role services.
shell:
cmd: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa role-show testrole
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: |
result.failed or not (
service1 in result.stdout
and service1 in result.stdout
and "user03" not in result.stdout
)
vars:
KRB5CCNAME: verify_issue_413
service1: "service01/{{ host1_fqdn }}"
service2: "service02/{{ host2_fqdn }}"
# End of test fix for https://github.com/freeipa/ansible-freeipa/issues/413
# Test fix for https://github.com/freeipa/ansible-freeipa/issues/411
- name: Add non-existing user to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user: nonexisiting_user
action: member
register: result
failed_when: not result.failed
# End of test fix for https://github.com/freeipa/ansible-freeipa/issues/411
# cleanup
- name: Cleanup environment.
include_tasks: env_cleanup.yml

View File

@@ -169,7 +169,7 @@ def list_test_yaml(dir_path):
`test_` and the extension is `.yml`. `test_` and the extension is `.yml`.
""" """
yamls = [] yamls = []
for root, dirs, files in os.walk(dir_path): for root, _dirs, files in os.walk(dir_path):
for yaml_name in files: for yaml_name in files:
if yaml_name.startswith("test_") and yaml_name.endswith(".yml"): if yaml_name.startswith("test_") and yaml_name.endswith(".yml"):
test_yaml_path = os.path.join(root, yaml_name) test_yaml_path = os.path.join(root, yaml_name)

View File

@@ -38,35 +38,35 @@
name: vaultgroup name: vaultgroup
state: absent state: absent
- name: Remove password file from target host. - name: Remove files from target host.
file: file:
path: "{{ ansible_env.HOME }}/password.txt" path: "{{ ansible_env.HOME }}/{{ item }}"
state: absent state: absent
with_items:
- A_private.pem
- A_public.pem
- B_private.pem
- B_public.pem
- A_private.b64
- A_public.b64
- B_private.b64
- B_public.b64
- password.txt
- in.txt
- out.txt
- name: Remove public key file from target host. - name: Remove files from controller.
file: file:
path: "{{ ansible_env.HOME }}/public.pem" path: "{{ playbook_dir }}/{{ item }}"
state: absent state: absent
- name: Remove private key file from target host.
file:
path: "{{ ansible_env.HOME }}/private.pem"
state: absent
- name: Remove output data file from target host.
file:
path: "{{ ansible_env.HOME }}/data.txt"
state: absent
- name: Remove input data file from target host.
file:
path: "{{ ansible_env.HOME }}/in.txt"
state: absent
- name: Remove private/public key files.
shell:
cmd: rm -f private.pem public.pem
delegate_to: localhost delegate_to: localhost
become: no become: no
args: with_items:
warn: no # suppres warning for not using the `file` module. - A_private.pem
- A_public.pem
- B_private.pem
- B_public.pem
- A_private.b64
- A_public.b64
- B_private.b64
- B_public.b64

View File

@@ -3,37 +3,34 @@
- name: Ensure environment is clean. - name: Ensure environment is clean.
import_tasks: env_cleanup.yml import_tasks: env_cleanup.yml
- name: Create private key file. - name: Create private/public key pair.
shell: shell:
cmd: openssl genrsa -out private.pem 2048 cmd: |
openssl genrsa -out "{{ item }}private.pem" 2048
openssl rsa -in "{{ item }}private.pem" -outform PEM -pubout -out "{{ item }}public.pem"
base64 "{{ item }}public.pem" -w5000 > "{{ item }}public.b64"
base64 "{{ item }}private.pem" -w5000 > "{{ item }}private.b64"
delegate_to: localhost delegate_to: localhost
become: no become: no
with_items:
- A_
- B_
- name: Create public key file. - name: Copy files to target host.
shell:
cmd: openssl rsa -in private.pem -outform PEM -pubout -out public.pem
delegate_to: localhost
become: no
- name: Copy password file to target host.
copy: copy:
src: "{{ playbook_dir }}/password.txt" src: "{{ playbook_dir }}/{{ item }}"
dest: "{{ ansible_env.HOME }}/password.txt" dest: "{{ ansible_env.HOME }}/{{ item }}"
with_items:
- name: Copy public key file to target host. - A_private.pem
copy: - A_public.pem
src: "{{ playbook_dir }}/public.pem" - B_private.pem
dest: "{{ ansible_env.HOME }}/public.pem" - B_public.pem
- A_private.b64
- name: Copy private key file to target host. - A_public.b64
copy: - B_private.b64
src: "{{ playbook_dir }}/private.pem" - B_public.b64
dest: "{{ ansible_env.HOME }}/private.pem" - password.txt
- in.txt
- name: Copy input data file to target host.
copy:
src: "{{ playbook_dir }}/in.txt"
dest: "{{ ansible_env.HOME }}/in.txt"
- name: Ensure vaultgroup exists. - name: Ensure vaultgroup exists.
ipagroup: ipagroup:

View File

@@ -25,9 +25,9 @@
- name: Ensure vault is present - name: Ensure vault is present
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: "{{vault.name}}" name: "{{ vault.name }}"
vault_type: "{{vault.vault_type}}" vault_type: "{{ vault.vault_type }}"
public_key: "{{lookup('file', 'private.pem', rstrip=False) | b64encode}}" public_key: "{{lookup('file', 'A_private.b64')}}"
register: result register: result
failed_when: not result.changed failed_when: not result.changed
when: vault.vault_type == 'asymmetric' when: vault.vault_type == 'asymmetric'

View File

@@ -14,18 +14,111 @@
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
vault_type: asymmetric vault_type: asymmetric
public_key: "{{ lookup('file', 'public.pem', rstrip=False) | b64encode }}" public_key: "{{ lookup('file', 'A_public.b64') }}"
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Ensure asymmetric vault is present, again - name: Ensure asymmetric vault is present, again
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
vault_type: asymmetric vault_type: asymmetric
public_key: "{{ lookup('file', 'public.pem', rstrip=False) | b64encode }}" public_key: "{{ lookup('file', 'A_public.b64') }}"
register: result register: result
failed_when: result.changed failed_when: result.failed or result.changed
- name: Archive data to asymmetric vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
vault_data: SomeValue
register: result
failed_when: result.failed or not result.changed
- name: Retrieve data from asymmetric vault using key A.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
private_key: "{{ lookup('file', 'A_private.b64') }}"
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'SomeValue'
- name: Change asymmetric vault key to B.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
vault_type: asymmetric
public_key: "{{ lookup('file', 'B_public.b64') }}"
private_key: "{{ lookup('file', 'A_private.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Retrieve data from asymmetric vault using key B.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'SomeValue'
- name: Change asymmetric vault key to A, using key_file
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
vault_type: asymmetric
public_key_file: "{{ ansible_env.HOME }}/A_public.pem"
private_key: "{{ lookup('file', 'B_private.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Retrieve data from asymmetric vault using key A, with key_file.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
private_key_file: "{{ ansible_env.HOME }}/A_private.pem"
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'SomeValue'
- name: Change asymmetric vault key to B key, using key_files
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
vault_type: asymmetric
public_key_file: "{{ ansible_env.HOME }}/B_public.pem"
private_key_file: "{{ ansible_env.HOME }}/A_private.pem"
register: result
failed_when: result.failed or not result.changed
- name: Retrieve data from asymmetric vault, using key B.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'SomeValue'
- name: Change asymmetric vault key to A, without specifying vault_type.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
vault_type: asymmetric
public_key: "{{ lookup('file', 'A_public.b64') }}"
private_key: "{{ lookup('file', 'B_private.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Change asymmetric vault key to B, with key files, without specifying vault_type.
ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
public_key_file: "{{ ansible_env.HOME }}/B_public.pem"
private_key_file: "{{ ansible_env.HOME }}/A_private.pem"
register: result
failed_when: result.failed or not result.changed
- name: Archive data to asymmetric vault, matching `no_log` field. - name: Archive data to asymmetric vault, matching `no_log` field.
ipavault: ipavault:
@@ -39,12 +132,12 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'SomeADMINpassword' or result.changed failed_when: result.vault.data != 'SomeADMINpassword' or result.changed
- name: Archive data to asymmetric vault - name: Change data in asymmetric vault
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
@@ -52,11 +145,11 @@
register: result register: result
failed_when: not result.changed failed_when: not result.changed
- name: Retrieve data from asymmetric vault. - name: Retrieve changed data from asymmetric vault.
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.vault.data != 'Hello World.' or result.changed
@@ -66,7 +159,7 @@
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
out: "{{ ansible_env.HOME }}/data.txt" out: "{{ ansible_env.HOME }}/data.txt"
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.changed or result.failed or (result.vault.data | default(false)) failed_when: result.changed or result.failed or (result.vault.data | default(false))
@@ -89,7 +182,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'The world of π is half rounded.' or result.changed failed_when: result.vault.data != 'The world of π is half rounded.' or result.changed
@@ -107,7 +200,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Another World.' or result.changed failed_when: result.vault.data != 'Another World.' or result.changed
@@ -124,7 +217,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'c' or result.changed failed_when: result.vault.data != 'c' or result.changed
@@ -149,7 +242,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
public_key_file: "{{ ansible_env.HOME }}/public.pem" public_key_file: "{{ ansible_env.HOME }}/B_public.pem"
vault_type: asymmetric vault_type: asymmetric
register: result register: result
failed_when: not result.changed failed_when: not result.changed
@@ -158,7 +251,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
public_key_file: "{{ ansible_env.HOME }}/public.pem" public_key_file: "{{ ansible_env.HOME }}/B_public.pem"
vault_type: asymmetric vault_type: asymmetric
register: result register: result
failed_when: result.changed failed_when: result.changed
@@ -175,7 +268,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key: "{{ lookup('file', 'private.pem', rstrip=False) | b64encode }}" private_key: "{{ lookup('file', 'B_private.b64') }}"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.vault.data != 'Hello World.' or result.changed
@@ -184,7 +277,7 @@
ipavault: ipavault:
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: asymvault name: asymvault
private_key_file: "{{ ansible_env.HOME }}/private.pem" private_key_file: "{{ ansible_env.HOME }}/B_private.pem"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.vault.data != 'Hello World.' or result.changed
@@ -206,4 +299,4 @@
failed_when: result.changed failed_when: result.changed
- name: Cleanup testing environment. - name: Cleanup testing environment.
import_tasks: env_setup.yml import_tasks: env_cleanup.yml

View File

@@ -0,0 +1,304 @@
---
- name: Test vault
hosts: ipaserver
become: true
# Need to gather facts for ansible_env.
gather_facts: true
tasks:
- name: Setup testing environment.
import_tasks: env_setup.yml
- name: Ensure test_vault is absent.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: absent
- name: Create standard vault with no data archived.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: standard
- name: Change from standard to asymmetric
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: asymmetric
public_key: "{{ lookup('file', 'A_public.b64') }}"
register: result
failed_when: result.failed or not result.changed
- block:
- name: Change from asymmetric to symmetric
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: symmetric
private_key: "{{ lookup('file', 'A_private.b64') }}"
password: SomeVAULTpassword
register: result
failed_when: result.failed or not result.changed
- name: Verify assymetric-only fields are not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Public Key:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_asymmetric
- block:
- name: Change from symmetric to standard
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: standard
password: SomeVAULTpassword
register: result
failed_when: result.failed or not result.changed
- name: Verify salt is not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Salt:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_symmetric
- name: Change from standard to symmetric
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: symmetric
password: SomeVAULTpassword
register: result
failed_when: result.failed or not result.changed
- block:
- name: Change from symmetric to asymmetric
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: asymmetric
password: SomeVAULTpassword
public_key: "{{ lookup('file', 'A_public.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Verify salt is not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Salt:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_symmetric
- block:
- name: Change from asymmetric to standard
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: standard
private_key: "{{ lookup('file', 'A_private.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Verify assymetric-only fields are not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Public Key:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_asymmetric
- name: Ensure test_vault is absent.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: absent
- name: Create standard vault with data archived.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: standard
data: hello
- name: Change from standard to asymmetric, with data
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: asymmetric
public_key: "{{ lookup('file', 'A_public.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Retrieve data from asymmetric vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
private_key: "{{ lookup('file', 'A_private.b64') }}"
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'hello'
- block:
- name: Change from asymmetric to symmetric, with data
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: symmetric
private_key: "{{ lookup('file', 'A_private.b64') }}"
password: SomeVAULTpassword
register: result
failed_when: result.failed or not result.changed
- name: Verify assymetric-only fields are not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Public Key:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_asymmetric
- name: Retrieve data from symmetric vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
password: SomeVAULTpassword
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'hello'
- block:
- name: Change from symmetric to standard, with data
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: standard
password: SomeVAULTpassword
register: result
failed_when: result.failed or not result.changed
- name: Verify salt is not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Salt:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_symmetric
- name: Retrieve data from standard vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'hello'
- name: Change from standard to symmetric, with data
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: symmetric
password: SomeVAULTpassword
register: result
failed_when: result.failed or not result.changed
- name: Retrieve data from symmetric vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: retrieved
password: SomeVAULTpassword
register: result
failed_when: result.failed or result.changed or result.vault.data != 'hello'
- block:
- name: Change from symmetric to asymmetric, with data
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: asymmetric
password: SomeVAULTpassword
public_key: "{{ lookup('file', 'A_public.b64') }}"
register: result
failed_when: result.failed or not result.changed
- name: Verify salt is not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Salt:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_symmetric
- name: Retrieve data from asymmetric vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: retrieved
private_key: "{{ lookup('file', 'A_private.b64') }}"
register: result
failed_when: result.failed or result.changed or result.vault.data != 'hello'
- block:
- name: Change from asymmetric to standard, with data
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
vault_type: standard
private_key: "{{ lookup('file', 'A_private.b64') }}"
register: result
failed_when: result.failed or not result.changed or result.failed
- name: Verify assymetric-only fields are not present.
shell: |
echo SomeADMINpassword | kinit -c {{ KRB5CCNAME }} admin
KRB5CCNAME={{ KRB5CCNAME }} ipa vault-show test_vault
kdestroy -A -q -c {{ KRB5CCNAME }}
register: result
failed_when: result.failed or "Public Key:" in result.stdout
vars:
KRB5CCNAME: verify_change_from_asymmetric
- name: Retrieve data from standard vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: retrieved
register: result
failed_when: result.failed or result.changed or result.vault.data != 'hello'
- name: Remove test_vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: test_vault
state: absent
- name: Cleanup testing environment.
import_tasks: env_cleanup.yml

View File

@@ -138,4 +138,4 @@
failed_when: result.changed failed_when: result.changed
- name: Cleanup testing environment. - name: Cleanup testing environment.
import_tasks: env_setup.yml import_tasks: env_cleanup.yml

View File

@@ -43,7 +43,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'SomeADMINpassword' or result.changed failed_when: result.changed or result.failed or result.vault.data != 'SomeADMINpassword'
- name: Archive data to symmetric vault - name: Archive data to symmetric vault
ipavault: ipavault:
@@ -61,7 +61,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.changed or result.failed or result.vault.data != 'Hello World.'
- name: Retrieve data from symmetric vault into file {{ ansible_env.HOME }}/data.txt. - name: Retrieve data from symmetric vault into file {{ ansible_env.HOME }}/data.txt.
ipavault: ipavault:
@@ -86,7 +86,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
vault_data: The world of π is half rounded. vault_data: The world of π is half rounded.
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Retrieve data from symmetric vault. - name: Retrieve data from symmetric vault.
ipavault: ipavault:
@@ -95,7 +95,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'The world of π is half rounded.' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'The world of π is half rounded.'
- name: Archive data in symmetric vault, from file. - name: Archive data in symmetric vault, from file.
ipavault: ipavault:
@@ -104,7 +104,7 @@
in: "{{ ansible_env.HOME }}/in.txt" in: "{{ ansible_env.HOME }}/in.txt"
password: SomeVAULTpassword password: SomeVAULTpassword
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Retrieve data from symmetric vault. - name: Retrieve data from symmetric vault.
ipavault: ipavault:
@@ -113,7 +113,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Another World.' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'Another World.'
- name: Archive data with single character to symmetric vault - name: Archive data with single character to symmetric vault
ipavault: ipavault:
@@ -122,7 +122,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
vault_data: c vault_data: c
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Retrieve data from symmetric vault. - name: Retrieve data from symmetric vault.
ipavault: ipavault:
@@ -131,7 +131,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'c' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'c'
- name: Ensure symmetric vault is absent - name: Ensure symmetric vault is absent
ipavault: ipavault:
@@ -139,7 +139,7 @@
name: symvault name: symvault
state: absent state: absent
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Ensure symmetric vault is absent, again - name: Ensure symmetric vault is absent, again
ipavault: ipavault:
@@ -147,7 +147,7 @@
name: symvault name: symvault
state: absent state: absent
register: result register: result
failed_when: result.changed failed_when: result.failed or result.changed
- name: Ensure symmetric vault is present, with password from file. - name: Ensure symmetric vault is present, with password from file.
ipavault: ipavault:
@@ -157,7 +157,7 @@
password_file: "{{ ansible_env.HOME }}/password.txt" password_file: "{{ ansible_env.HOME }}/password.txt"
vault_type: symmetric vault_type: symmetric
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Ensure symmetric vault is present, with password from file, again. - name: Ensure symmetric vault is present, with password from file, again.
ipavault: ipavault:
@@ -167,7 +167,7 @@
password_file: "{{ ansible_env.HOME }}/password.txt" password_file: "{{ ansible_env.HOME }}/password.txt"
vault_type: symmetric vault_type: symmetric
register: result register: result
failed_when: result.changed failed_when: result.failed or result.changed
- name: Archive data to symmetric vault - name: Archive data to symmetric vault
ipavault: ipavault:
@@ -176,7 +176,7 @@
vault_data: Hello World. vault_data: Hello World.
password: SomeVAULTpassword password: SomeVAULTpassword
register: result register: result
failed_when: not result.changed failed_when: not result.changed or result.failed
- name: Retrieve data from symmetric vault. - name: Retrieve data from symmetric vault.
ipavault: ipavault:
@@ -185,7 +185,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'Hello World.'
- name: Retrieve data from symmetric vault, with password file. - name: Retrieve data from symmetric vault, with password file.
ipavault: ipavault:
@@ -194,7 +194,7 @@
password_file: "{{ ansible_env.HOME }}/password.txt" password_file: "{{ ansible_env.HOME }}/password.txt"
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'Hello World.'
- name: Retrieve data from symmetric vault, with wrong password. - name: Retrieve data from symmetric vault, with wrong password.
ipavault: ipavault:
@@ -203,7 +203,7 @@
password: SomeWRONGpassword password: SomeWRONGpassword
state: retrieved state: retrieved
register: result register: result
failed_when: not result.failed or "Invalid credentials" not in result.msg failed_when: result.changed or not result.failed or "Invalid credentials" not in result.msg
- name: Change vault password. - name: Change vault password.
ipavault: ipavault:
@@ -212,7 +212,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
new_password: SomeNEWpassword new_password: SomeNEWpassword
register: result register: result
failed_when: not result.changed failed_when: not result.changed or result.failed
- name: Retrieve data from symmetric vault, with new password. - name: Retrieve data from symmetric vault, with new password.
ipavault: ipavault:
@@ -221,7 +221,7 @@
password: SomeNEWpassword password: SomeNEWpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'Hello World.'
- name: Retrieve data from symmetric vault, with old password. - name: Retrieve data from symmetric vault, with old password.
ipavault: ipavault:
@@ -240,7 +240,7 @@
new_password: SomeVAULTpassword new_password: SomeVAULTpassword
salt: AAAAAAAAAAAAAAAAAAAAAAA= salt: AAAAAAAAAAAAAAAAAAAAAAA=
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Change symmetric vault salt, without changing password - name: Change symmetric vault salt, without changing password
ipavault: ipavault:
@@ -250,7 +250,7 @@
new_password: SomeVAULTpassword new_password: SomeVAULTpassword
salt: MTIzNDU2Nzg5MDEyMzQ1Ngo= salt: MTIzNDU2Nzg5MDEyMzQ1Ngo=
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Try to change symmetric vault salt, without providing any password - name: Try to change symmetric vault salt, without providing any password
ipavault: ipavault:
@@ -258,7 +258,7 @@
name: symvault name: symvault
salt: MTIzNDU2Nzg5MDEyMzQ1Ngo= salt: MTIzNDU2Nzg5MDEyMzQ1Ngo=
register: result register: result
failed_when: not result.failed and "Vault `salt` can only change when changing the password." 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 `password` - name: Try to change symmetric vault salt, without providing `password`
ipavault: ipavault:
@@ -294,7 +294,7 @@
name: symvault name: symvault
state: absent state: absent
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Ensure symmetric vault is absent, again - name: Ensure symmetric vault is absent, again
ipavault: ipavault:
@@ -302,7 +302,7 @@
name: symvault name: symvault
state: absent state: absent
register: result register: result
failed_when: result.changed failed_when: result.failed or result.changed
- name: Try to change password of inexistent vault. - name: Try to change password of inexistent vault.
ipavault: ipavault:
@@ -340,7 +340,7 @@
password: SomeVAULTpassword password: SomeVAULTpassword
state: retrieved state: retrieved
register: result register: result
failed_when: result.vault.data != 'Hello World.' or result.changed failed_when: result.failed or result.changed or result.vault.data != 'Hello World.'
- name: Ensure symmetric vault is absent - name: Ensure symmetric vault is absent
ipavault: ipavault:
@@ -348,7 +348,7 @@
name: symvault name: symvault
state: absent state: absent
register: result register: result
failed_when: not result.changed failed_when: result.failed or not result.changed
- name: Cleanup testing environment. - name: Cleanup testing environment.
import_tasks: env_cleanup.yml import_tasks: env_cleanup.yml

56
utils/build-galaxy-release.sh Normal file → Executable file
View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
namespace="${1:freeipa}" namespace="${1-freeipa}"
collection="${2:ansible_freeipa}" collection="${2-ansible_freeipa}"
collection_prefix="${namespace}.${collection}" collection_prefix="${namespace}.${collection}"
galaxy_version=$(git describe --tags | sed -e "s/^v//") galaxy_version=$(git describe --tags | sed -e "s/^v//")
@@ -14,46 +14,50 @@ find . -name "*~" -exec rm {} \;
sed -i -e "s/ansible.module_utils.ansible_freeipa_module/ansible_collections.${collection_prefix}.plugins.module_utils.ansible_freeipa_module/" plugins/modules/*.py sed -i -e "s/ansible.module_utils.ansible_freeipa_module/ansible_collections.${collection_prefix}.plugins.module_utils.ansible_freeipa_module/" plugins/modules/*.py
cd plugins/module_utils && { (cd plugins/module_utils && {
ln -s ../../roles/*/module_utils/*.py . ln -s ../../roles/*/module_utils/*.py .
cd ../.. })
}
cd plugins/modules && { (cd plugins/modules && {
sed -i -e "s/ansible.module_utils.ansible_ipa_/ansible_collections.${collection_prefix}.plugins.module_utils.ansible_ipa_/" ../../roles/*/library/*.py sed -i -e "s/ansible.module_utils.ansible_ipa_/ansible_collections.${collection_prefix}.plugins.module_utils.ansible_ipa_/" ../../roles/*/library/*.py
ln -s ../../roles/*/library/*.py . ln -s ../../roles/*/library/*.py .
cd ../.. })
}
[ ! -x plugins/action_plugins ] && mkdir plugins/action_plugins [ ! -x plugins/action_plugins ] && mkdir plugins/action_plugins
cd plugins/action_plugins && { (cd plugins/action_plugins && {
ln -s ../../roles/*/action_plugins/*.py . ln -s ../../roles/*/action_plugins/*.py .
cd ../.. })
}
for x in $(find plugins/modules -name "*.py" -print); do find plugins/modules -name "*.py" -print0 |
python utils/galaxyfy-module-EXAMPLES.py "$x" "ipa" "$collection_prefix" while IFS= read -d -r '' line; do
done python utils/galaxyfy-module-EXAMPLES.py "$x" \
"ipa" "$collection_prefix"
done
for x in $(find roles/*/library -name "*.py" -print); do find roles/*/library -name "*.py" -print0 |
python utils/galaxyfy-module-EXAMPLES.py "$x" "ipa" "$collection_prefix" while IFS= read -d -r '' line; do
done python utils/galaxyfy-module-EXAMPLES.py "$x" \
"ipa" "$collection_prefix"
done
for x in roles/*/tasks/*.yml; do for x in roles/*/tasks/*.yml; do
python utils/galaxyfy-playbook.py "$x" "ipa" "$collection_prefix" python utils/galaxyfy-playbook.py "$x" "ipa" "$collection_prefix"
done done
for x in $(find playbooks -name "*.yml" -print); do find playbooks -name "*.yml" -print0 |
python utils/galaxyfy-playbook.py "$x" "ipa" "$collection_prefix" while IFS= read -d -r '' line; do
done python utils/galaxyfy-playbook.py "$x" "ipa" "$collection_prefix"
done
for x in $(find . -name "README*.md" -print); do find . -name "README*.md" -print0 |
python utils/galaxyfy-README.py "$x" "ipa" "$collection_prefix" while IFS= read -d -r '' line; do
done python utils/galaxyfy-README.py "$x" "ipa" "$collection_prefix"
done
for x in $(find tests -name "*.yml" -print); do find tests -name "*.yml" -print0 |
python utils/galaxyfy-playbook.py "$x" "ipa" "$collection_prefix" while IFS= read -d -r '' line; do
done python utils/galaxyfy-playbook.py "$x" "ipa" "$collection_prefix"
done
#git diff #git diff

View File

@@ -87,7 +87,7 @@ def store(commits, prs, authors, commit, author, merge, msg):
def get_commit(commits, commit): def get_commit(commits, commit):
_commits = [value for key, value in commits.items() _commits = [value for key, value in commits.items()
if key.startswith(merge)] if key.startswith(commit)]
if len(_commits) == 1: if len(_commits) == 1:
return _commits[0] return _commits[0]
return commit return commit

View File

@@ -1,3 +1,5 @@
#!/bin/bash
for i in roles/ipa*/*/*.py; do for i in roles/ipa*/*/*.py; do
python utils/gen_module_docs.py $i python utils/gen_module_docs.py $i
done done

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
topdir=`dirname $(dirname $0)` topdir="`dirname $(dirname $0)`"
flake8 . flake8 .
pydocstyle . pydocstyle .
@@ -11,15 +11,18 @@ ANSIBLE_MODULE_UTILS=${ANSIBLE_MODULE_UTILS:-"${topdir}/plugins/module_utils"}
export ANSIBLE_LIBRARY ANSIBLE_MODULE_UTILS export ANSIBLE_LIBRARY ANSIBLE_MODULE_UTILS
yaml_dirs=( yaml_dirs=(
"${topdir}/tests/*.yml" "${topdir}/tests"
"${topdir}/tests/*/*.yml" "${topdir}/playbooks"
"${topdir}/tests/*/*/*.yml" "${topdir}/molecule"
"${topdir}/playbooks/*.yml"
"${topdir}/playbooks/*/*.yml"
"${topdir}/molecule/*/*.yml"
"${topdir}/molecule/*/*/*.yml"
) )
ansible-lint --force-color ${yaml_dirs[@]} for dir in "${yaml_dirs[@]}"
do
find "${dir}" -type f -name "*.yml" | xargs ansible-lint --force-color
done
yamllint -f colored ${yaml_dirs[@]}
for dir in "${yaml_dirs[@]}"
do
find "${dir}" -type f -name "*.yml" | xargs yamllint
done

View File

@@ -73,7 +73,7 @@ author=$2
email=$3 email=$3
year=$(date +"%Y") year=$(date +"%Y")
if [ -z "$name" -o -z "$author" -o -z "$email" ]; then if [ -z "$name" ] || [ -z "$author" ] || [ -z "$email" ]; then
[ -z "$name" ] && echo "ERROR: name is not valid" [ -z "$name" ] && echo "ERROR: name is not valid"
[ -z "$author" ] && echo "ERROR: author is not valid" [ -z "$author" ] && echo "ERROR: author is not valid"
[ -z "$email" ] && echo "ERROR: email is not valid" [ -z "$email" ] && echo "ERROR: email is not valid"

View File

@@ -286,6 +286,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands:

View File

@@ -207,6 +207,10 @@ def main():
else: else:
ansible_module.fail_json(msg="Unkown state '%s'" % state) ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands # Execute commands
for name, command, args in commands: for name, command, args in commands: