mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-30 15:23:06 +00:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1930d8c8be | ||
|
|
1837ee662c | ||
|
|
26e171df79 | ||
|
|
01440e3c04 | ||
|
|
2426e04c22 | ||
|
|
92e44f6a6c | ||
|
|
16c8ee87e9 | ||
|
|
3109e9d1bc | ||
|
|
b457de545d | ||
|
|
f1a6f44477 | ||
|
|
1dbe19cefb | ||
|
|
7982fad342 | ||
|
|
212719496c | ||
|
|
3de6f9146e | ||
|
|
48f2ef88a4 | ||
|
|
6845acd596 | ||
|
|
ba7bf0f6cd | ||
|
|
fe2d17e4df | ||
|
|
319a0d3d86 | ||
|
|
c71a2b33dd | ||
|
|
02223dfb67 | ||
|
|
5731a1539b | ||
|
|
ee7354230b | ||
|
|
4bb40f3397 | ||
|
|
55b8729c52 | ||
|
|
539ace413d | ||
|
|
0c20b34d28 | ||
|
|
f9ff41320f | ||
|
|
69c6b4d644 | ||
|
|
b63716b724 | ||
|
|
3cf138674b | ||
|
|
12e0d110f6 | ||
|
|
34654d1090 | ||
|
|
72d3ab8e04 | ||
|
|
fb75aed663 | ||
|
|
6f5bb9eebf | ||
|
|
e5b2c122ce | ||
|
|
c0692e1746 | ||
|
|
2d079c8eec | ||
|
|
b70a1ecf61 | ||
|
|
7cb5e481e5 | ||
|
|
60593b7dd3 | ||
|
|
e84ed3b6ba | ||
|
|
6e1f9f1a72 | ||
|
|
46a307aaeb | ||
|
|
d8f8211a1c | ||
|
|
34daa992f5 | ||
|
|
07c1a5ee61 | ||
|
|
63d0272385 | ||
|
|
d0a8005a7f | ||
|
|
24efad73fa | ||
|
|
fd1352ad7e | ||
|
|
de38e8f0bc | ||
|
|
847ae2a374 | ||
|
|
bcee9aba92 | ||
|
|
c34c66fa79 | ||
|
|
0a3cd06c6e | ||
|
|
b5b22c3f7e | ||
|
|
7ee385ee02 | ||
|
|
7d9e4da9df | ||
|
|
0a20b5902d | ||
|
|
be9a2db404 | ||
|
|
ba4a360520 | ||
|
|
3534fcdce7 | ||
|
|
f0f21fc8aa | ||
|
|
5ed96eda05 | ||
|
|
cf779e43bb | ||
|
|
1a48a0fb63 | ||
|
|
ed3a0d5a1b | ||
|
|
d58b492f1d | ||
|
|
88d4a36e17 | ||
|
|
6fa8223662 | ||
|
|
c9e8656494 | ||
|
|
a791c6a0ca | ||
|
|
9cbccdade9 | ||
|
|
42c07d6336 | ||
|
|
a728a8d43e | ||
|
|
bd3266e9f1 | ||
|
|
48063d2b3a | ||
|
|
5d08214516 | ||
|
|
ef0b7e80f0 | ||
|
|
a33fcf45f8 | ||
|
|
c4b273c896 | ||
|
|
62d34d0a22 | ||
|
|
3ed0c229c4 | ||
|
|
c089c010e6 | ||
|
|
cfbdd83a64 | ||
|
|
fef1bdcf8e | ||
|
|
411d363d91 | ||
|
|
1555132d85 | ||
|
|
57ad57dda3 | ||
|
|
dab64c7cf6 | ||
|
|
b7145bc2cc | ||
|
|
c9f1da5d6b | ||
|
|
f4070f6a30 | ||
|
|
ad9a03ece6 | ||
|
|
1bfe6888a4 | ||
|
|
51ddaa6491 | ||
|
|
f56861cc15 | ||
|
|
c4de680497 | ||
|
|
7b2701b985 | ||
|
|
694c717829 | ||
|
|
083396e133 | ||
|
|
9a8a1db38f | ||
|
|
8f9c344bc1 | ||
|
|
067b683b81 | ||
|
|
51f64e4393 |
2
.github/workflows/ansible-test.yml
vendored
2
.github/workflows/ansible-test.yml
vendored
@@ -11,7 +11,5 @@ jobs:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install virtualenv using pip
|
||||
run: pip install virtualenv
|
||||
- name: Run ansible-test
|
||||
run: bash tests/sanity/sanity.sh
|
||||
|
||||
36
.github/workflows/docs.yml
vendored
36
.github/workflows/docs.yml
vendored
@@ -5,23 +5,6 @@ on:
|
||||
- pull_request
|
||||
jobs:
|
||||
check_docs_oldest_supported:
|
||||
name: Check Ansible Documentation with ansible-core 2.12.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v4.3.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Ansible 2.12
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.12,<2.13"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_previous:
|
||||
name: Check Ansible Documentation with ansible-core 2.13.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -38,7 +21,7 @@ jobs:
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_current:
|
||||
check_docs_previous:
|
||||
name: Check Ansible Documentation with ansible-core 2.14.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -55,6 +38,23 @@ jobs:
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_current:
|
||||
name: Check Ansible Documentation with ansible-core 2.15.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v4.3.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Ansible 2.15
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.15,<2.16"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_ansible_latest:
|
||||
name: Check Ansible Documentation with latest Ansible version.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -16,10 +16,10 @@ jobs:
|
||||
python-version: "3.x"
|
||||
- name: Run ansible-lint
|
||||
run: |
|
||||
pip install "ansible-core >=2.14,<2.15" ansible-lint
|
||||
pip install "ansible-core>=2.16,<2.17" 'ansible-lint>=6.21'
|
||||
utils/build-galaxy-release.sh -ki
|
||||
cd .galaxy-build
|
||||
ansible-lint
|
||||
ansible-lint --profile production --exclude tests/integration/ --exclude tests/unit/ --parseable --nocolor
|
||||
|
||||
yamllint:
|
||||
name: Verify yamllint
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
python-version: "3.x"
|
||||
- name: Run pylint
|
||||
run: |
|
||||
pip install pylint==2.14.4 wrapt==1.14.0
|
||||
pip install 'pylint>=3.0'
|
||||
pylint plugins roles --disable=import-error
|
||||
|
||||
shellcheck:
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/ansible/ansible-lint.git
|
||||
rev: v6.6.1
|
||||
rev: v6.22.0
|
||||
hooks:
|
||||
- id: ansible-lint
|
||||
always_run: false
|
||||
pass_filenames: true
|
||||
files: \.(yaml|yml)$
|
||||
exclude: /env[^/]*.(yaml|yml)$
|
||||
entry: |
|
||||
env ANSIBLE_LIBRARY=./plugins/modules ANSIBLE_MODULE_UTILS=./plugins/module_utils ANSIBLE_DOC_FRAGMENT_PLUGINS=./plugins/doc_fragments ansible-lint
|
||||
entry: |-
|
||||
env
|
||||
ANSIBLE_LIBRARY=./plugins/modules
|
||||
ANSIBLE_MODULE_UTILS=./plugins/module_utils
|
||||
ANSIBLE_DOC_FRAGMENT_PLUGINS=./plugins/doc_fragments
|
||||
ansible-lint
|
||||
--offline
|
||||
--profile production
|
||||
--exclude tests/integration/
|
||||
--exclude tests/unit/
|
||||
--parseable
|
||||
--nocolor
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.28.0
|
||||
rev: v1.32.0
|
||||
hooks:
|
||||
- id: yamllint
|
||||
files: \.(yaml|yml)$
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 5.0.3
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/pycqa/pydocstyle
|
||||
@@ -24,7 +34,7 @@ repos:
|
||||
hooks:
|
||||
- id: pydocstyle
|
||||
- repo: https://github.com/pycqa/pylint
|
||||
rev: v2.14.4
|
||||
rev: v3.0.2
|
||||
hooks:
|
||||
- id: pylint
|
||||
args:
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipaautomountkey module.
|
||||
Requirements
|
||||
------------
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipaautomountlocation module.
|
||||
Requirements
|
||||
------------
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipaautomountmap module.
|
||||
Requirements
|
||||
------------
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -54,6 +54,21 @@ Example playbook to ensure presence of an automount map:
|
||||
desc: "this is a map for servers in the DMZ"
|
||||
```
|
||||
|
||||
Automount maps can contain a submount key, which defines a mount location within the map the references another map. On FreeIPA, this is known as an indirect map. An indirect automount map is equivalent to adding a proper automount key to a map, referencyng another map (this second map is the indirect map). Use `parent` and `mount` parameters to create an indirect automount map with ansible-freeipa, without the need to directly manage the automount keys.
|
||||
|
||||
Example playbook to ensure an indirect automount map is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to add an indirect automount map
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.indirect
|
||||
location: DMZ
|
||||
parent: auto.DMZ
|
||||
mount: dmz_indirect
|
||||
```
|
||||
|
||||
Example playbook to ensure auto.DMZi is absent:
|
||||
|
||||
```yaml
|
||||
@@ -81,16 +96,14 @@ 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
|
||||
`name` \| `mapname` \| `map` \| `automountmapname` | Name of the map to manage | yes
|
||||
`location` \| `automountlocation` \| `automountlocationcn` | Location name. | yes
|
||||
`parentmap` | Parent map of the indirect map. Can only be used when creating new maps. Default: auto.master | no
|
||||
`mount` | Indirect map mount point, relative to parent map. | yes, if `parent` is used.
|
||||
`desc` \| `description` | Description of the map | yes
|
||||
`state` | The state to ensure. It can be one of `present`, or `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Creation of indirect mount points are not supported.
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Chris Procter
|
||||
- Chris Procter
|
||||
- Rafael Jeffman
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
* Some tool to generate a certificate signing request (CSR) might be needed, like `openssl`.
|
||||
|
||||
**Node**
|
||||
@@ -77,6 +77,23 @@ Example playbook to revoke an existing certificate:
|
||||
ipacert:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
serial_number: 123456789
|
||||
reason: 5
|
||||
state: revoked
|
||||
```
|
||||
|
||||
When revoking a certificate a mnemonic can also be used to set the revocation reason:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Revoke certificate
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name Revoke a certificate
|
||||
ipacert:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
serial_number: 123456789
|
||||
reason: cessationOfOperation
|
||||
state: revoked
|
||||
```
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -145,7 +145,7 @@ Variable | Description | Required
|
||||
`selinuxusermaporder` \| `ipaselinuxusermaporder`| Set ordered list in increasing priority of SELinux users | no
|
||||
`selinuxusermapdefault`\| `ipaselinuxusermapdefault` | Set default SELinux user when no match is found in SELinux map rule | no
|
||||
`pac_type` \| `ipakrbauthzdata` | set default types of PAC supported for services (choices: `MS-PAC`, `PAD`, `nfs:NONE`). Use `""` to clear this variable. | no
|
||||
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `disabled`). Use `""` to clear this variable. | no
|
||||
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp`, `disabled`, `""`). An additional check ensures that only types can be used that are supported by the IPA version. Use `""` to clear this variable. | no
|
||||
`domain_resolution_order` \| `ipadomainresolutionorder` | Set list of domains used for short name qualification | no
|
||||
`ca_renewal_master_server` \| `ipacarenewalmasterserver`| Renewal master for IPA certificate authority. | no
|
||||
`enable_sid` | New users and groups automatically get a SID assigned. Cannot be deactivated once activated. Requires IPA 4.9.8+. (bool) | no
|
||||
|
||||
@@ -23,7 +23,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipadnsforwardzone module.
|
||||
Requirements
|
||||
------------
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -23,7 +23,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
|
||||
**Node**
|
||||
|
||||
@@ -29,7 +29,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -335,7 +335,7 @@ Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`description` | The host description. | no
|
||||
`locality` | Host locality (e.g. "Baltimore, MD"). | no
|
||||
`location` \| `ns_host_location` | Host location (e.g. "Lab 2"). | no
|
||||
`location` \| `ns_host_location` | Host physical location hint (e.g. "Lab 2"). | no
|
||||
`platform` \| `ns_hardware_platform` | Host hardware platform (e.g. "Lenovo T61"). | no
|
||||
`os` \| `ns_os_version` | Host operating system and version (e.g. "Fedora 9"). | no
|
||||
`password` \| `user_password` \| `userpassword` | Password used in bulk enrollment for absent or not enrolled hosts. | no
|
||||
@@ -354,7 +354,7 @@ Variable | Description | Required
|
||||
`mac_address` \| `macaddress` | List of hardware MAC addresses. | no
|
||||
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys | no
|
||||
`userclass` \| `class` | Host category (semantics placed on this attribute are for local interpretation) | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened", ""] | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. An additional check ensures that only types can be used that are supported by the IPA version. Choices: ["radius", "otp", "pkinit", "hardened", "idp", ""] | no
|
||||
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service (bool) | no
|
||||
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service (bool) | no
|
||||
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no
|
||||
|
||||
@@ -26,7 +26,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
233
README-idoverridegroup.md
Normal file
233
README-idoverridegroup.md
Normal file
@@ -0,0 +1,233 @@
|
||||
Idoverridegroup module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The idoverridegroup module allows to ensure presence and absence of idoverridegroups and idoverridegroup members.
|
||||
|
||||
|
||||
Use Cases
|
||||
---------
|
||||
|
||||
With idoverridegroup it is possible to manage group attributes within ID views. These attributes are for example the group name or gid.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Idoverridegroup management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaidoverridegroup module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.13
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview.
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview with description
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview with description
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
description: "test_group description"
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview without description
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview without description
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
description: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview with internal name test_123_group
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview with internal name test_123_group
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
name: test_123_group
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview without internal name
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
- name: Ensure test group test_group is present in idview test_idview without internal name
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
name: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview with gid 20001
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview with gid 20001
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
gid: 20001
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview without gid
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview without gid
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
gid: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is present in idview test_idview with enabling falling back to AD DC LDAP when resolving AD trusted objects. (For two-way trusts only.)
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is present in idview test_idview with fallback_to_ldap enabled
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
fallback_to_ldap: true
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test group test_group is absent in idview test_idview
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test group test_group is absent in idview test_idview
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
continue: true
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | no
|
||||
`idview` \| `idviewcn` | The doverridegroup idview string. | yes
|
||||
`anchor` \| `ipaanchoruuid` | The list of anchors to override. | yes
|
||||
`description` \| `desc` | Description | no
|
||||
`name` \| `group_name` \| `cn` | The group. | no
|
||||
`gid` \| `gidnumber` | Group ID Number (int or "") | no
|
||||
`fallback_to_ldap` | Allow falling back to AD DC LDAP when resolving AD trusted objects. For two-way trusts only. | no
|
||||
`delete_continue` \| `continue` | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
503
README-idoverrideuser.md
Normal file
503
README-idoverrideuser.md
Normal file
@@ -0,0 +1,503 @@
|
||||
Idoverrideuser module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The idoverrideuser module allows to ensure presence and absence of idoverrideusers and idoverrideuser members.
|
||||
|
||||
|
||||
Use Cases
|
||||
---------
|
||||
|
||||
With idoverrideuser it is possible to manage user attributes within ID views. These attributes are for example the login name, home directory, certificate for authentication or SSH keys.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Idoverrideuser management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaidoverrideuser module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.13
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview.
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with description
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with description
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
description: "test_user description"
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without description
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without description
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
description: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with internal name test_123_user
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with internal name test_123_user
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
name: test_123_user
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without internal name
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without internal name
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
name: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with uid 20001
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with uid 20001
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
uid: 20001
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without uid
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without uid
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
uid: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with gecos "Gecos Test"
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with gecos "Gecos Test"
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gecos: Gecos Test
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without gecos
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without gecos
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gecos: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with gidnumber
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with gidnumber
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gidnumber: 20001
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without gidnumber
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without gidnumber
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gidnumber: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with homedir /Users
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with homedir /Users
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
homedir: /Users
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without homedir
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without homedir
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
homedir: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with shell
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with shell
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
shell: /bin/someshell
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without shell
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without shell
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
shell: ""
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with sshpubkey
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with sshpubkey
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
sshpubkey:
|
||||
- ssh-rsa AAAAB3NzaC1yc2EAAADAQABAAABgQCqmVDpEX5gnSjKuv97Ay ...
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without sshpubkey
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without sshpubkey
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
sshpubkey: []
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with 1 certificate
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with 1 certificate
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with 3 certificate members
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with 3 certificate members
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
|
||||
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
|
||||
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
|
||||
action: member
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without 2 certificate members
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without 2 certificate members
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
|
||||
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview without certificates
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview without certificates
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate: []
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is present in idview test_idview with enabling falling back to AD DC LDAP when resolving AD trusted objects. (For two-way trusts only.)
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview with fallback_to_ldap enabled
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
fallback_to_ldap: true
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure test user test_user is absent in idview test_idview
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is absent in idview test_idview
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
continue: true
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | no
|
||||
`idview` \| `idviewcn` | The doverrideuser idview string. | yes
|
||||
`anchor` \| `ipaanchoruuid` | The list of anchors to override. | yes
|
||||
`description` \| `desc` | Description | no
|
||||
`name` \| `login` | The user (internally uid) | no
|
||||
`uid` \| `uidnumber` | User ID Number (int or "") | no
|
||||
`gecos` | GECOS | no
|
||||
`gidnumber` | Group ID Number (int or ""). | no
|
||||
`homedir` \| `homedirectory` | Home directory. | no
|
||||
`shell` \| `loginshell` | Login shell. | no
|
||||
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no
|
||||
`certificate` \| `usercertificate` | List of Base-64 encoded user certificates. This variable can also be used with `action: member`. | no
|
||||
`fallback_to_ldap` | Allow falling back to AD DC LDAP when resolving AD trusted objects. For two-way trusts only. | no
|
||||
`delete_continue` \| `continue` | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
|
||||
`nomembers` \| `no_members` | Suppress processing of membership attributes. Valid only if `state` is `absent`. | no
|
||||
`action` | Work on idoverrideuser or member level. It can be on of `member` or `idoverrideuser` and defaults to `idoverrideuser`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
192
README-idp.md
Normal file
192
README-idp.md
Normal file
@@ -0,0 +1,192 @@
|
||||
Idp module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The idp module allows to ensure presence and absence of idps.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Idp management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaidp module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.13
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure keycloak idp my-keycloak-idp is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idp.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure keycloak idp my-keycloak-idp is present
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-keycloak-idp
|
||||
provider: keycloak
|
||||
organization: main
|
||||
base_url: keycloak.idm.example.com:8443/auth
|
||||
client_id: my-client-id
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure keycloak idp my-keycloak-idp is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idp.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure keycloak idp my-keycloak-idp is absent
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-keycloak-idp
|
||||
delete_continue: true
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure github idp my-github-idp is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idp.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure github idp my-github-idp is present
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-github-idp
|
||||
provider: github
|
||||
client_id: my-github-client-id
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure google idp my-google-idp is present using provider defaults without specifying provider:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idp.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure google idp my-google-idp is present using provider defaults without specifying provider
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-google-idp
|
||||
auth_uri: https://accounts.google.com/o/oauth2/auth
|
||||
dev_auth_uri: https://oauth2.googleapis.com/device/code
|
||||
token_uri: https://oauth2.googleapis.com/token
|
||||
keys_uri: https://www.googleapis.com/oauth2/v3/certs
|
||||
userinfo_uri: https://openidconnect.googleapis.com/v1/userinfo
|
||||
client_id: my-google-client-id
|
||||
scope: "openid email"
|
||||
idp_user_id: email
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure google idp my-google-idp is present using provider:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idp.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure google idp my-google-idp is present using provider
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-google-idp
|
||||
provider: google
|
||||
client_id: my-google-client-id
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure idps my-keycloak-idp, my-github-idp and my-google-idp are absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idp.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure idps my-keycloak-idp, my-github-idp and my-google-idp are absent
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name:
|
||||
- my-keycloak-idp
|
||||
- my-github-idp
|
||||
- my-google-idp
|
||||
delete_continue: true
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | false
|
||||
`name` \| `cn` | The list of idp name strings. | yes
|
||||
auth_uri \| ipaidpauthendpoint | OAuth 2.0 authorization endpoint string. | no
|
||||
dev_auth_uri \| ipaidpdevauthendpoint | Device authorization endpoint string. | no
|
||||
token_uri \| ipaidptokenendpoint | Token endpoint string. | no
|
||||
userinfo_uri \| ipaidpuserinfoendpoint | User information endpoint string. | no
|
||||
keys_uri \| ipaidpkeysendpoint | JWKS endpoint string. | no
|
||||
issuer_url \| ipaidpissuerurl | The Identity Provider OIDC URL string. | no
|
||||
client_id \| ipaidpclientid | OAuth 2.0 client identifier string. | no
|
||||
secret \| ipaidpclientsecret | OAuth 2.0 client secret string. | no
|
||||
scope \| ipaidpscope | OAuth 2.0 scope string. Multiple scopes separated by space. | no
|
||||
idp_user_id \| ipaidpsub | Attribute string for user identity in OAuth 2.0 userinfo. | no
|
||||
provider \| ipaidpprovider | Pre-defined template string. This provides the provider defaults, which can be overridden with the other IdP options. Choices: ["google","github","microsoft","okta","keycloak"] | no
|
||||
organization \| ipaidporg | Organization ID string or Realm name for IdP provider templates. | no
|
||||
base_url \| ipaidpbaseurl | Base URL string for IdP provider templates. | no
|
||||
rename \| new_name | New name for the Identity Provider server object. Only with `state: renamed`. | no
|
||||
delete_continue \| continue | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, `renamed`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
@@ -37,7 +37,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
153
README-idview.md
Normal file
153
README-idview.md
Normal file
@@ -0,0 +1,153 @@
|
||||
Idview module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The idview module allows to ensure presence and absence of idviews and idview host members.
|
||||
|
||||
Use Cases
|
||||
---------
|
||||
|
||||
With ID views it is possible to override user or group attributes for users stored in the LDAP server. For example the login name, home directory, certificate for authentication or SSH keys. An ID view is client-side and specifies new values for user or group attributes and also the client host or hosts on which the values apply.
|
||||
|
||||
The ID view and the applied hosts are managed with idview, the user attributes are managed with idoverrideuser and the group attributes with idoverridegroup.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Idview management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaidview module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.13
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure idview "test_idview" is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idview.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure idview "test_idview" member host "testhost.example.com" is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idview host member.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
host: testhost.example.com
|
||||
action: member
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure idview "test_idview" member host "testhost.example.com" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idview host member.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
host: testhost.example.com
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure idview "test_idview" is present with domain_resolution_order for "ad.example.com:ipa.example.com":
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idview host member.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
domain_resolution_order: "ad.example.com:ipa.example.com"
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure idview "test_idview" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idview.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | no
|
||||
`name` \| `cn` | The list of idview name strings. | yes
|
||||
`description` \| `desc` | The description string of the idview. | no
|
||||
`domain_resolution_order` \| `ipadomainresolutionorder` | Colon-separated list of domains used for short name qualification. | no
|
||||
`host` \| `hosts` | List of hosts to apply the ID View to. A host can only be applied to a single idview at any time. Applying a host that is already applied to a different idview will change the idview the host is applied to to the new one. | no
|
||||
`rename` \| `new_name` | Rename the ID view object to the new name string. Only usable with `state: renamed`. | no
|
||||
`delete_continue` \| `continue` | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
|
||||
`action` | Work on idview or member level. It can be on of `member` or `idview` and defaults to `idview`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent` and `renamed`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -128,20 +128,20 @@ Variable | Description | Required
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to yes. (bool) | no
|
||||
`name` \| `cn` | The list of pwpolicy name strings. If name is not given, `global_policy` will be used automatically. | no
|
||||
`maxlife` \| `krbmaxpwdlife` | Maximum password lifetime in days. (int) | no
|
||||
`minlife` \| `krbminpwdlife` | Minimum password lifetime in hours. (int) | no
|
||||
`history` \| `krbpwdhistorylength` | Password history size. (int) | no
|
||||
`minclasses` \| `krbpwdmindiffchars` | Minimum number of character classes. (int) | no
|
||||
`minlength` \| `krbpwdminlength` | Minimum length of password. (int) | no
|
||||
`priority` \| `cospriority` | Priority of the policy, higher number means lower priority. (int) | no
|
||||
`maxfail` \| `krbpwdmaxfailure` | Consecutive failures before lockout. (int) | no
|
||||
`failinterval` \| `krbpwdfailurecountinterval` | Period after which failure count will be reset in seconds. (int) | no
|
||||
`lockouttime` \| `krbpwdlockoutduration` | Period for which lockout is enforced in seconds. (int) | no
|
||||
`maxrepeat` \| `ipapwdmaxrepeat` | Maximum number of same consecutive characters. Requires IPA 4.9+ (int) | no
|
||||
`maxsequence` \| `ipapwdmaxsequence` | The maximum length of monotonic character sequences (abcd). Requires IPA 4.9+ (int) | no
|
||||
`dictcheck` \| `ipapwdictcheck` | Check if the password is a dictionary word. Requires IPA 4.9+ (int) | no
|
||||
`usercheck` \| `ipapwdusercheck` | Check if the password contains the username. Requires IPA 4.9+ (int) | no
|
||||
`gracelimit` \| `passwordgracelimit` | Number of LDAP authentications allowed after expiration. Requires IPA 4.9.10 (int) | no
|
||||
`maxlife` \| `krbmaxpwdlife` | Maximum password lifetime in days. (int or "") | no
|
||||
`minlife` \| `krbminpwdlife` | Minimum password lifetime in hours. (int or "") | no
|
||||
`history` \| `krbpwdhistorylength` | Password history size. (int or "") | no
|
||||
`minclasses` \| `krbpwdmindiffchars` | Minimum number of character classes. (int or "") | no
|
||||
`minlength` \| `krbpwdminlength` | Minimum length of password. (int or "") | no
|
||||
`priority` \| `cospriority` | Priority of the policy, higher number means lower priority. (int or "") | no
|
||||
`maxfail` \| `krbpwdmaxfailure` | Consecutive failures before lockout. (int or "") | no
|
||||
`failinterval` \| `krbpwdfailurecountinterval` | Period after which failure count will be reset in seconds. (int or "") | no
|
||||
`lockouttime` \| `krbpwdlockoutduration` | Period for which lockout is enforced in seconds. (int or "") | no
|
||||
`maxrepeat` \| `ipapwdmaxrepeat` | Maximum number of same consecutive characters. Requires IPA 4.9+ (int or "") | no
|
||||
`maxsequence` \| `ipapwdmaxsequence` | The maximum length of monotonic character sequences (abcd). Requires IPA 4.9+ (int or "") | no
|
||||
`dictcheck` \| `ipapwdictcheck` | Check if the password is a dictionary word. Requires IPA 4.9+. (bool or "") | no
|
||||
`usercheck` \| `ipapwdusercheck` | Check if the password contains the username. Requires IPA 4.9+. (bool or "") | no
|
||||
`gracelimit` \| `passwordgracelimit` | Number of LDAP authentications allowed after expiration. Requires IPA 4.9.10 (int or "") | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -23,7 +23,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -249,14 +249,14 @@ Variable | Description | Required
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to yes. (bool) | no
|
||||
`name` \| `cn` | The list of server name strings. | yes
|
||||
`location` \| `ipalocation_location` | The server location string. Only in state: present. "" for location reset. | no
|
||||
`service_weight` \| `ipaserviceweight` | Weight for server services. Type Values 0 to 65535, -1 for weight reset. Only in state: present. (int) | no
|
||||
`hidden` | Set hidden state of a server. Only in state: present. (bool) | no
|
||||
`no_members` | Suppress processing of membership attributes. Only in state: present. (bool) | no
|
||||
`delete_continue` \| `continue` | Continuous mode: Don't stop on errors. Only in state: absent. (bool) | no
|
||||
`ignore_last_of_role` | Skip a check whether the last CA master or DNS server is removed. Only in state: absent. (bool) | no
|
||||
`ignore_topology_disconnect` | Ignore topology connectivity problems after removal. Only in state: absent. (bool) | no
|
||||
`force` | Force server removal even if it does not exist. Will always result in changed. Only in state: absent. (bool) | no
|
||||
`location` \| `ipalocation_location` | The server DNS location. Only available with 'state: present'. Use "" for location reset. | no
|
||||
`service_weight` \| `ipaserviceweight` | Weight for server services. Type Values 0 to 65535, -1 for weight reset. Only available with 'state: present'. (int) | no
|
||||
`hidden` | Set hidden state of a server. Only available with 'state: present'. (bool) | no
|
||||
`no_members` | Suppress processing of membership attributes. Only avialable with 'state: present'. (bool) | no
|
||||
`delete_continue` \| `continue` | Continuous mode: Don't stop on errors. Only available with 'state: absent'. (bool) | no
|
||||
`ignore_last_of_role` | Skip a check whether the last CA master or DNS server is removed. Only available with 'state: absent'. (bool) | no
|
||||
`ignore_topology_disconnect` | Ignore topology connectivity problems after removal. Only available with 'state: absent'. (bool) | no
|
||||
`force` | Force server removal even if it does not exist. Will always result in changed. Only available with 'state: absent'. (bool) | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. `present` is only working with existing servers. | no
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FReeIPA version (see above)
|
||||
@@ -294,7 +294,7 @@ Variable | Description | Required
|
||||
`name` \| `service` | The list of service name strings. | yes
|
||||
`certificate` \| `usercertificate` | Base-64 encoded service certificate. | no
|
||||
`pac_type` \| `ipakrbauthzdata` | Supported PAC type. It can be one of `MS-PAC`, `PAD`, or `NONE`. Use empty string to reset pac_type to the initial value. | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit` or `hardened`. Use empty string to reset auth_ind to the initial value. | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, `hardened`, `idp` or `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset auth_ind to the initial value. | no
|
||||
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service. Default to true. (bool) | no
|
||||
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service. Default to false. (bool) | no
|
||||
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client. Default to false. (bool) | no
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -22,7 +22,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -21,7 +21,7 @@ Requirements
|
||||
|
||||
**Controller**
|
||||
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -58,6 +58,7 @@ Example playbook to ensure a user is present:
|
||||
last: Acme
|
||||
uid: 10001
|
||||
gid: 100
|
||||
gecos: "The Pinky"
|
||||
phone: "+555123457"
|
||||
email: pinky@acme.com
|
||||
passwordexpiration: "2023-01-19 23:59:59"
|
||||
@@ -352,6 +353,33 @@ Example playbook to ensure users are absent:
|
||||
state: absent
|
||||
```
|
||||
|
||||
When using FreeIPA 4.8.0+, SMB logon script, profile, home directory and home drive can be set for users.
|
||||
|
||||
In the example playbook to set SMB attributes note that `smb_profile_path` and `smb_home_dir` use paths in UNC format, which includes backslashes ('\\`). If the paths are quoted, the backslash needs to be escaped becoming "\\", so the path `\\server\dir` becomes `"\\\\server\\dir"`. If the paths are unquoted the slashes do not have to be escaped.
|
||||
|
||||
The YAML specification states that a colon (':') is a key separator and a dash ('-') is an item marker, only with a space after them, so using both unquoted as part of a path should not be a problem. If a space is needed after a colon or a dash, then a quoted string must be used as in `"user - home"`. For the `smb_home_drive` attribute is is recomended that a quoted string is used, to improve readability.
|
||||
|
||||
Example playbook to set SMB attributes:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Plabook to handle users
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure user 'smbuser' is present with smb attributes
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: smbuser
|
||||
first: SMB
|
||||
last: User
|
||||
smb_logon_script: N:\logonscripts\startup
|
||||
smb_profile_path: \\server\profiles\some_profile
|
||||
smb_home_dir: \\users\home\smbuser
|
||||
smb_home_drive: "U:"
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
@@ -395,6 +423,8 @@ Variable | Description | Required
|
||||
`random` | Generate a random user password | no
|
||||
`uid` \| `uidnumber` | User ID Number (system will assign one if not provided). | no
|
||||
`gid` \| `gidnumber` | Group ID Number. | no
|
||||
`gecos` | GECOS | no
|
||||
`street` | Street address | no
|
||||
`city` | City | no
|
||||
`userstate` \| `st` | State/Province | no
|
||||
`postalcode` \| `zip` | Postalcode/ZIP | no
|
||||
@@ -407,7 +437,7 @@ Variable | Description | Required
|
||||
`manager` | List of manager user names. | no
|
||||
`carlicense` | List of car licenses. | no
|
||||
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no
|
||||
`userauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp` and ``. Use empty string to reset userauthtype to the initial value. | no
|
||||
`userauthtype` \| `ipauserauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp` and `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset userauthtype to the initial value. | no
|
||||
`userclass` | User category. (semantics placed on this attribute are for local interpretation). | no
|
||||
`radius` | RADIUS proxy configuration | no
|
||||
`radiususer` | RADIUS proxy username | no
|
||||
@@ -415,6 +445,8 @@ Variable | Description | Required
|
||||
`employeenumber` | Employee Number | no
|
||||
`employeetype` | Employee Type | no
|
||||
`preferredlanguage` | Preferred Language | no
|
||||
`idp` \| `ipaidpconfiglink` | External IdP configuration | no
|
||||
`idp_user_id` \| `ipaidpsub` | A string that identifies the user at external IdP | no
|
||||
`certificate` | List of base-64 encoded user certificates. | no
|
||||
`certmapdata` | List of certificate mappings. Either `data` or `certificate` or `issuer` together with `subject` need to be specified. Only usable with IPA versions 4.5 and up. <br>Options: | no
|
||||
| `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no
|
||||
@@ -422,6 +454,10 @@ Variable | Description | Required
|
||||
| `subject` - Subject of the certificate, only usable together with `issuer` option. | no
|
||||
| `data` - Certmap data, not usable with other certmapdata options. | no
|
||||
`noprivate` | Do not create user private group. (bool) | no
|
||||
`smb_logon_script` \| `ipantlogonscript` | SMB logon script path. Requires FreeIPA version 4.8.0+. | no
|
||||
`smb_profile_path:` \| `ipantprofilepath` | SMB profile path, in UNC format. Requires FreeIPA version 4.8.0+. | no
|
||||
`smb_home_dir` \| `ipanthomedirectory` | SMB Home Directory, in UNC format. Requires FreeIPA version 4.8.0+. | no
|
||||
`smb_home_drive` \| `ipanthomedirectorydrive` | SMB Home Directory Drive, a single upercase letter (A-Z) followed by a colon (:), for example "U:". Requires FreeIPA version 4.8.0+. | no
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
|
||||
|
||||
@@ -442,3 +478,4 @@ Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
Rafael Jeffman
|
||||
|
||||
@@ -24,7 +24,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
20
README.md
20
README.md
@@ -30,7 +30,11 @@ Features
|
||||
* Modules for hbacsvcgroup management
|
||||
* Modules for host management
|
||||
* Modules for hostgroup management
|
||||
* Modules for idoverridegroup management
|
||||
* Modules for idoverrideuser management
|
||||
* Modules for idp management
|
||||
* Modules for idrange management
|
||||
* Modules for idview management
|
||||
* Modules for location management
|
||||
* Modules for netgroup management
|
||||
* Modules for permission management
|
||||
@@ -69,7 +73,7 @@ Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection)
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -129,18 +133,8 @@ This command will get the whole collection from galaxy:
|
||||
ansible-galaxy collection install freeipa.ansible_freeipa
|
||||
```
|
||||
|
||||
Installing collections using the ansible-galaxy command is only supported with ansible 2.9+.
|
||||
|
||||
The mazer tool can be used for to install the collection for ansible 2.8:
|
||||
|
||||
```bash
|
||||
mazer install freeipa.ansible_freeipa
|
||||
```
|
||||
|
||||
Ansible galaxy does not support the use of dash ('-') in a name and is automatically replacing this with an underscore ('\_'). Therefore the name is `ansible_freeipa`. The ansible_freeipa collection will be placed in the directory `~/.ansible/collections/ansible_collections/freeipa/ansible_freeipa` where it will be automatically be found for this user.
|
||||
|
||||
The needed adaptions of collection prefixes for `modules` and `module_utils` will be done with ansible-freeipa release `0.1.6` for galaxy.
|
||||
|
||||
|
||||
Ansible inventory file
|
||||
----------------------
|
||||
@@ -450,7 +444,11 @@ Modules in plugin/modules
|
||||
* [ipahbacsvcgroup](README-hbacsvcgroup.md)
|
||||
* [ipahost](README-host.md)
|
||||
* [ipahostgroup](README-hostgroup.md)
|
||||
* [idoverridegroup](README-idoverridegroup.md)
|
||||
* [idoverrideuser](README-idoverrideuser.md)
|
||||
* [idp](README-idp.md)
|
||||
* [idrange](README-idrange.md)
|
||||
* [idview](README-idview.md)
|
||||
* [ipalocation](README-location.md)
|
||||
* [ipanetgroup](README-netgroup.md)
|
||||
* [ipapermission](README-permission.md)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
requires_ansible: ">=2.9"
|
||||
requires_ansible: ">=2.13"
|
||||
|
||||
14
playbooks/automount/automount-map-indirect-map.yml
Normal file
14
playbooks/automount/automount-map-indirect-map.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Managed automount maps
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Playbook to add an indirect automount map
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.indirect
|
||||
location: DMZ
|
||||
parent: auto.DMZ
|
||||
mount: dmz_indirect
|
||||
13
playbooks/idoverridegroup/idoverridegroup-absent.yml
Normal file
13
playbooks/idoverridegroup/idoverridegroup-absent.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure idoverridegroup test_group is absent in idview test_idview.
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
continue: true
|
||||
state: absent
|
||||
11
playbooks/idoverridegroup/idoverridegroup-present.yml
Normal file
11
playbooks/idoverridegroup/idoverridegroup-present.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to manage idoverridegroup
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure idoverridegroup test_group is present in idview test_idview.
|
||||
ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
13
playbooks/idoverrideuser/idoverrideuser-absent.yml
Normal file
13
playbooks/idoverrideuser/idoverrideuser-absent.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is absent in idview test_idview
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
continue: true
|
||||
state: absent
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user certificate member is absent in idview test_idview
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
|
||||
action: member
|
||||
state: absent
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user certificate member is present in idview test_idview
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
|
||||
action: member
|
||||
11
playbooks/idoverrideuser/idoverrideuser-present.yml
Normal file
11
playbooks/idoverrideuser/idoverrideuser-present.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to manage idoverrideuser
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure test user test_user is present in idview test_idview.
|
||||
ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
11
playbooks/idp/idp-absent.yml
Normal file
11
playbooks/idp/idp-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Idp absent example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure github idp my-github-idp is absent
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-github-idp
|
||||
state: absent
|
||||
12
playbooks/idp/idp-present.yml
Normal file
12
playbooks/idp/idp-present.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Idp present example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure github idp my-github-idp is present
|
||||
ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-github-idp
|
||||
provider: github
|
||||
client_id: my-github-client-id
|
||||
11
playbooks/idview/idview-absent.yml
Normal file
11
playbooks/idview/idview-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Idview absent example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure idview test_idview is absent
|
||||
ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
state: absent
|
||||
12
playbooks/idview/idview-host-applied.yml
Normal file
12
playbooks/idview/idview-host-applied.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Idview host member applied example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure host testhost.example.com is applied to idview test_idview
|
||||
ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
host: testhost.example.com
|
||||
action: member
|
||||
13
playbooks/idview/idview-host-unapplied.yml
Normal file
13
playbooks/idview/idview-host-unapplied.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Idview host member unapplied example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure host testhost.example.com is not applied to idview test_idview
|
||||
ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
host: testhost.example.com
|
||||
action: member
|
||||
state: absent
|
||||
10
playbooks/idview/idview-present.yml
Normal file
10
playbooks/idview/idview-present.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Idview present example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure idview test_idview is present
|
||||
ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
12
playbooks/user/add-user-external-idp.yml
Normal file
12
playbooks/user/add-user-external-idp.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Create user associated with an external IdP
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: idpuser
|
||||
idp: keycloak
|
||||
idp_user_id: idpuser@exemple.com
|
||||
17
playbooks/user/smb-attributes.yml
Normal file
17
playbooks/user/smb-attributes.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: Plabook to handle users
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure user 'smbuser' is present with smb attributes
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: smbuser
|
||||
first: SMB
|
||||
last: User
|
||||
smb_logon_script: N:\logonscripts\startup
|
||||
smb_profile_path: \\server\profiles\some_profile
|
||||
smb_home_dir: \\users\home\smbuser
|
||||
smb_home_drive: "U:"
|
||||
@@ -30,7 +30,7 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
|
||||
"kinit_password", "kinit_keytab", "run", "DN", "VERSION",
|
||||
"paths", "tasks", "get_credentials_if_valid", "Encoding",
|
||||
"DNSName", "getargspec", "certificate_loader",
|
||||
"write_certificate_list"]
|
||||
"write_certificate_list", "boolean", "template_str"]
|
||||
|
||||
import os
|
||||
# ansible-freeipa requires locale to be C, IPA requires utf-8.
|
||||
@@ -42,6 +42,7 @@ import tempfile
|
||||
import shutil
|
||||
import socket
|
||||
import base64
|
||||
import ast
|
||||
from datetime import datetime
|
||||
from contextlib import contextmanager
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
@@ -49,6 +50,7 @@ from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.common.text.converters import jsonify
|
||||
from ansible.module_utils import six
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
|
||||
# Import getargspec from inspect or provide own getargspec for
|
||||
# Python 2 compatibility with Python 3.11+.
|
||||
@@ -88,6 +90,7 @@ try:
|
||||
except ImportError:
|
||||
from ipapython.ipautil import kinit_password, kinit_keytab
|
||||
from ipapython.ipautil import run
|
||||
from ipapython.ipautil import template_str
|
||||
from ipapython.dn import DN
|
||||
from ipapython.version import VERSION
|
||||
from ipaplatform.paths import paths
|
||||
@@ -224,7 +227,7 @@ def temp_kdestroy(ccache_dir, ccache_name):
|
||||
"""Destroy temporary ticket and remove temporary ccache."""
|
||||
if ccache_name is not None:
|
||||
run([paths.KDESTROY, '-c', ccache_name], raiseonerr=False)
|
||||
del os.environ['KRB5CCNAME']
|
||||
os.environ.pop('KRB5CCNAME', None)
|
||||
if ccache_dir is not None:
|
||||
shutil.rmtree(ccache_dir, ignore_errors=True)
|
||||
|
||||
@@ -1168,6 +1171,45 @@ class IPAAnsibleModule(AnsibleModule):
|
||||
"""
|
||||
return api_check_param(command, name)
|
||||
|
||||
def ipa_command_invalid_param_choices(self, command, name, value):
|
||||
"""
|
||||
Return invalid parameter choices for IPA command.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
command: string
|
||||
The IPA API command to test.
|
||||
name: string
|
||||
The parameter name to check.
|
||||
value: string
|
||||
The parameter value to verify.
|
||||
|
||||
"""
|
||||
if command not in api.Command:
|
||||
self.fail_json(msg="The command '%s' does not exist." % command)
|
||||
if name not in api.Command[command].params:
|
||||
self.fail_json(msg="The command '%s' does not have a parameter "
|
||||
"named '%s'." % (command, name))
|
||||
if not hasattr(api.Command[command].params[name], "cli_metavar"):
|
||||
self.fail_json(msg="The parameter '%s' of the command '%s' does "
|
||||
"not have choices." % (name, command))
|
||||
# For IPA 4.6 (RHEL-7):
|
||||
# - krbprincipalauthind in host_add does not have choices defined
|
||||
# - krbprincipalauthind in service_add does not have choices defined
|
||||
#
|
||||
# api.Command[command].params[name].cli_metavar returns "STR" and
|
||||
# ast.literal_eval failes with a ValueError "malformed string".
|
||||
#
|
||||
# There is no way to verify that the given values are valid or not in
|
||||
# this case. The check is done later on while applying the change
|
||||
# with host_add, host_mod, service_add and service_mod.
|
||||
try:
|
||||
_choices = ast.literal_eval(
|
||||
api.Command[command].params[name].cli_metavar)
|
||||
except ValueError:
|
||||
return None
|
||||
return (set(value or []) - set([""])) - set(_choices)
|
||||
|
||||
@staticmethod
|
||||
def ipa_check_version(oper, requested_version):
|
||||
"""
|
||||
|
||||
@@ -121,8 +121,7 @@ class AutomountKey(IPAAnsibleModule):
|
||||
resp = self.ipa_command("automountkey_show", location, args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
else:
|
||||
return resp.get("result")
|
||||
return resp.get("result")
|
||||
|
||||
def check_ipa_params(self):
|
||||
invalid = []
|
||||
|
||||
@@ -92,8 +92,7 @@ class AutomountLocation(IPAAnsibleModule):
|
||||
)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
else:
|
||||
return response.get("result", None)
|
||||
return response.get("result", None)
|
||||
|
||||
def check_ipa_params(self):
|
||||
if len(self.params_get("name")) == 0:
|
||||
|
||||
@@ -37,6 +37,7 @@ module: ipaautomountmap
|
||||
author:
|
||||
- Chris Procter (@chr15p)
|
||||
- Thomas Woerner (@t-woerner)
|
||||
- Rafael Jeffman (@rjeffman)
|
||||
short_description: Manage FreeIPA autommount map
|
||||
description:
|
||||
- Add, delete, and modify an IPA automount map
|
||||
@@ -59,6 +60,16 @@ options:
|
||||
type: str
|
||||
aliases: ["description"]
|
||||
required: false
|
||||
parentmap:
|
||||
description: |
|
||||
Parent map of the indirect map. Can only be used when creating
|
||||
new maps.
|
||||
type: str
|
||||
required: false
|
||||
mount:
|
||||
description: Indirect map mount point, relative to parent map.
|
||||
type: str
|
||||
required: false
|
||||
state:
|
||||
description: State to ensure
|
||||
type: str
|
||||
@@ -75,6 +86,14 @@ EXAMPLES = '''
|
||||
location: DMZ
|
||||
desc: "this is a map for servers in the DMZ"
|
||||
|
||||
- name: ensure indirect map exists
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.INDIRECT
|
||||
location: DMZ
|
||||
parentmap: auto.DMZ
|
||||
mount: indirect
|
||||
|
||||
- name: remove a map named auto.DMZ in location DMZ if it exists
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
@@ -107,8 +126,36 @@ class AutomountMap(IPAAnsibleModule):
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
else:
|
||||
return response["result"]
|
||||
return response["result"]
|
||||
|
||||
def get_indirect_map_keys(self, location, name):
|
||||
"""Check if 'name' is an indirect map for 'parentmap'."""
|
||||
try:
|
||||
maps = self.ipa_command("automountmap_find", location, {})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return []
|
||||
|
||||
result = []
|
||||
for check_map in maps.get("result", []):
|
||||
_mapname = check_map['automountmapname'][0]
|
||||
keys = self.ipa_command(
|
||||
"automountkey_find",
|
||||
location,
|
||||
{
|
||||
"automountmapautomountmapname": _mapname,
|
||||
"all": True
|
||||
}
|
||||
)
|
||||
cmp_value = (
|
||||
name if _mapname == "auto.master" else "ldap:{0}".format(name)
|
||||
)
|
||||
result.extend([
|
||||
(location, _mapname, key.get("automountkey")[0])
|
||||
for key in keys.get("result", [])
|
||||
for mount_info in key.get("automountinformation", [])
|
||||
if cmp_value in mount_info
|
||||
])
|
||||
return result
|
||||
|
||||
def check_ipa_params(self):
|
||||
invalid = []
|
||||
@@ -118,15 +165,27 @@ class AutomountMap(IPAAnsibleModule):
|
||||
if len(name) != 1:
|
||||
self.fail_json(msg="Exactly one name must be provided for"
|
||||
" 'state: present'.")
|
||||
mount = self.params_get("mount") or False
|
||||
parentmap = self.params_get("parentmap")
|
||||
if parentmap:
|
||||
if not mount:
|
||||
self.fail_json(
|
||||
msg="Must provide 'mount' parameter for indirect map."
|
||||
)
|
||||
elif parentmap != "auto.master" and mount[0] == "/":
|
||||
self.fail_json(
|
||||
msg="mount point is relative to parent map, "
|
||||
"cannot begin with '/'"
|
||||
)
|
||||
if state == "absent":
|
||||
if len(name) == 0:
|
||||
self.fail_json(msg="At least one 'name' must be provided for"
|
||||
" 'state: absent'")
|
||||
invalid = ["desc"]
|
||||
invalid = ["desc", "parentmap", "mount"]
|
||||
|
||||
self.params_fail_used_invalid(invalid, state)
|
||||
|
||||
def get_args(self, mapname, desc):
|
||||
def get_args(self, mapname, desc, parentmap, mount):
|
||||
# automountmapname is required for all automountmap operations.
|
||||
if not mapname:
|
||||
self.fail_json(msg="automountmapname cannot be None or empty.")
|
||||
@@ -134,6 +193,11 @@ class AutomountMap(IPAAnsibleModule):
|
||||
# An empty string is valid and will clear the attribute.
|
||||
if desc is not None:
|
||||
_args["description"] = desc
|
||||
# indirect map attributes
|
||||
if parentmap is not None:
|
||||
_args["parentmap"] = parentmap
|
||||
if mount is not None:
|
||||
_args["key"] = mount
|
||||
return _args
|
||||
|
||||
def define_ipa_commands(self):
|
||||
@@ -141,28 +205,102 @@ class AutomountMap(IPAAnsibleModule):
|
||||
state = self.params_get("state")
|
||||
location = self.params_get("location")
|
||||
desc = self.params_get("desc")
|
||||
mount = self.params_get("mount")
|
||||
parentmap = self.params_get("parentmap")
|
||||
|
||||
for mapname in name:
|
||||
automountmap = self.get_automountmap(location, mapname)
|
||||
|
||||
is_indirect_map = any([parentmap, mount])
|
||||
|
||||
if state == "present":
|
||||
args = self.get_args(mapname, desc)
|
||||
args = self.get_args(mapname, desc, parentmap, mount)
|
||||
if automountmap is None:
|
||||
self.commands.append([location, "automountmap_add", args])
|
||||
if is_indirect_map:
|
||||
if (
|
||||
parentmap and
|
||||
self.get_automountmap(location, parentmap) is None
|
||||
):
|
||||
self.fail_json(msg="Parent map does not exist.")
|
||||
self.commands.append(
|
||||
[location, "automountmap_add_indirect", args]
|
||||
)
|
||||
else:
|
||||
self.commands.append(
|
||||
[location, "automountmap_add", args]
|
||||
)
|
||||
else:
|
||||
if not compare_args_ipa(self, args, automountmap):
|
||||
has_changes = not compare_args_ipa(
|
||||
self, args, automountmap, ['parentmap', 'key']
|
||||
)
|
||||
if is_indirect_map:
|
||||
map_config = (
|
||||
location, parentmap or "auto.master", mount
|
||||
)
|
||||
indirects = self.get_indirect_map_keys(
|
||||
location, mapname
|
||||
)
|
||||
if map_config not in indirects or has_changes:
|
||||
self.fail_json(
|
||||
msg="Indirect maps can only be created, "
|
||||
"not modified."
|
||||
)
|
||||
elif has_changes:
|
||||
self.commands.append(
|
||||
[location, "automountmap_mod", args]
|
||||
)
|
||||
|
||||
if state == "absent":
|
||||
elif state == "absent":
|
||||
def find_keys(parent_loc, parent_map, parent_key):
|
||||
return self.ipa_command(
|
||||
"automountkey_show",
|
||||
parent_loc,
|
||||
{
|
||||
"automountmapautomountmapname": parent_map,
|
||||
"automountkey": parent_key,
|
||||
}
|
||||
).get("result")
|
||||
|
||||
if automountmap is not None:
|
||||
indirects = self.get_indirect_map_keys(location, mapname)
|
||||
# Remove indirect map configurations for this map
|
||||
self.commands.extend([
|
||||
(
|
||||
ploc,
|
||||
"automountkey_del",
|
||||
{
|
||||
"automountmapautomountmapname": pmap,
|
||||
"automountkey": pkey,
|
||||
}
|
||||
)
|
||||
for ploc, pmap, pkey in indirects
|
||||
if find_keys(ploc, pmap, pkey)
|
||||
])
|
||||
# Remove map
|
||||
self.commands.append([
|
||||
location,
|
||||
"automountmap_del",
|
||||
{"automountmapname": [mapname]}
|
||||
])
|
||||
|
||||
# ensure commands are unique and automountkey commands are
|
||||
# executed first in the list
|
||||
def hashable_dict(dictionaire):
|
||||
return tuple(
|
||||
(k, tuple(v) if isinstance(v, (list, tuple)) else v)
|
||||
for k, v in dictionaire.items()
|
||||
)
|
||||
|
||||
cmds = [
|
||||
(name, cmd, hashable_dict(args))
|
||||
for name, cmd, args in self.commands
|
||||
]
|
||||
self.commands = [
|
||||
(name, cmd, dict(args))
|
||||
for name, cmd, args in
|
||||
sorted(set(cmds), key=lambda cmd: cmd[1])
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
ipa_module = AutomountMap(
|
||||
@@ -184,6 +322,10 @@ def main():
|
||||
required=False,
|
||||
default=None
|
||||
),
|
||||
parentmap=dict(
|
||||
type="str", required=False, default=None
|
||||
),
|
||||
mount=dict(type="str", required=False, default=None),
|
||||
),
|
||||
)
|
||||
changed = False
|
||||
|
||||
@@ -160,7 +160,8 @@ options:
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
choices: ["password", "radius", "otp", "disabled", ""]
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp",
|
||||
"disabled", ""]
|
||||
aliases: ["ipauserauthtype"]
|
||||
ca_renewal_master_server:
|
||||
description: Renewal master for IPA certificate authority.
|
||||
@@ -357,8 +358,7 @@ def get_netbios_name(module):
|
||||
_result = module.ipa_command_no_name("trustconfig_show", {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
else:
|
||||
return _result["result"]["ipantflatname"][0]
|
||||
return _result["result"]["ipantflatname"][0]
|
||||
|
||||
|
||||
def is_enable_sid(module):
|
||||
@@ -425,6 +425,7 @@ def main():
|
||||
choices=["MS-PAC", "PAD", "nfs:NONE", ""]),
|
||||
user_auth_type=dict(type="list", elements="str", required=False,
|
||||
choices=["password", "radius", "otp",
|
||||
"pkinit", "hardened", "idp",
|
||||
"disabled", ""],
|
||||
aliases=["ipauserauthtype"]),
|
||||
ca_renewal_master_server=dict(type="str", required=False),
|
||||
@@ -475,7 +476,7 @@ def main():
|
||||
params = {}
|
||||
for x in field_map:
|
||||
val = ansible_module.params_get(
|
||||
x, allow_empty_string=(x in allow_empty_string))
|
||||
x, allow_empty_string=x in allow_empty_string)
|
||||
|
||||
if val is not None:
|
||||
params[field_map.get(x, x)] = val
|
||||
@@ -525,6 +526,15 @@ def main():
|
||||
result = config_show(ansible_module)
|
||||
|
||||
if params:
|
||||
# Verify ipauserauthtype(s)
|
||||
if "ipauserauthtype" in params and params["ipauserauthtype"]:
|
||||
_invalid = ansible_module.ipa_command_invalid_param_choices(
|
||||
"config_mod", "ipauserauthtype", params["ipauserauthtype"])
|
||||
if _invalid:
|
||||
ansible_module.fail_json(
|
||||
msg="The use of userauthtype '%s' is not "
|
||||
"supported by your IPA version" % "','".join(_invalid))
|
||||
|
||||
enable_sid = params.get("enable_sid")
|
||||
sid_is_enabled = has_enable_sid and is_enable_sid(ansible_module)
|
||||
|
||||
@@ -609,7 +619,7 @@ def main():
|
||||
# boolean values, so we need to convert it to str
|
||||
# for comparison.
|
||||
# See: https://github.com/freeipa/freeipa/pull/6294
|
||||
exit_args[k] = (str(value[0]).upper() == "TRUE")
|
||||
exit_args[k] = str(value[0]).upper() == "TRUE"
|
||||
else:
|
||||
if arg_type not in type_map:
|
||||
raise ValueError(
|
||||
|
||||
@@ -134,8 +134,7 @@ def find_delegation(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if delegation name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(permission, attribute, membergroup, group):
|
||||
|
||||
@@ -258,7 +258,7 @@ def main():
|
||||
invalid = [
|
||||
"forwarders", "forwardpolicy", "skip_overlap_check", "permission"
|
||||
]
|
||||
wants_enable = (state == "enabled")
|
||||
wants_enable = state == "enabled"
|
||||
|
||||
if operation == "del":
|
||||
invalid = [
|
||||
|
||||
@@ -1453,7 +1453,7 @@ def define_commands_for_present_state(module, zone_name, entry, res_find):
|
||||
else:
|
||||
# Create reverse records for existing records
|
||||
for ipv in ['a', 'aaaa']:
|
||||
record = ('%srecord' % ipv)
|
||||
record = '%srecord' % ipv
|
||||
if record in args and ('%s_extra_create_reverse' % ipv) in args:
|
||||
cmds = create_reverse_ip_record(
|
||||
module, zone_name, name, args[record])
|
||||
|
||||
@@ -186,7 +186,17 @@ def find_hbacrule(module, name):
|
||||
module.fail_json(
|
||||
msg="There is more than one hbacrule '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
return _result["result"][0]
|
||||
res = _result["result"][0]
|
||||
# hbacsvcgroup names are converted to lower case while creation with
|
||||
# hbacsvcgroup_add.
|
||||
# The hbacsvcgroup for sudo is builtin with the name "Sudo" though.
|
||||
# This breaks the lower case comparison. Therefore all
|
||||
# memberservice_hbacsvcgroup items are converted to lower case if
|
||||
# "Sudo" is in the list.
|
||||
_member = "memberservice_hbacsvcgroup"
|
||||
if _member in res and "Sudo" in res[_member]:
|
||||
res[_member] = [item.lower() for item in res[_member]]
|
||||
return res
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -146,21 +146,6 @@ def gen_member_args(hbacsvc):
|
||||
return _args
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors):
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
if "failed" in result and "member" in result["failed"]:
|
||||
failed = result["failed"]["member"]
|
||||
for member_type in failed:
|
||||
for member, failure in failed[member_type]:
|
||||
if "already a member" not in failure \
|
||||
and "not a member" not in failure:
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
@@ -303,7 +288,8 @@ def main():
|
||||
}])
|
||||
|
||||
# Execute commands
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
# Done
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ options:
|
||||
type: str
|
||||
required: false
|
||||
location:
|
||||
description: Host location (e.g. "Lab 2")
|
||||
description: Host physical location hist (e.g. "Lab 2")
|
||||
type: str
|
||||
aliases: ["ns_host_location"]
|
||||
required: false
|
||||
@@ -184,7 +184,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["krbprincipalauthind"]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", ""]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
required: false
|
||||
requires_pre_auth:
|
||||
description: Pre-authentication is required for the service
|
||||
@@ -356,7 +356,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["krbprincipalauthind"]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", ""]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
required: false
|
||||
requires_pre_auth:
|
||||
description: Pre-authentication is required for the service
|
||||
@@ -667,6 +667,15 @@ def check_parameters( # pylint: disable=unused-argument
|
||||
module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
|
||||
def check_authind(module, auth_ind):
|
||||
_invalid = module.ipa_command_invalid_param_choices(
|
||||
"host_add", "krbprincipalauthind", auth_ind)
|
||||
if _invalid:
|
||||
module.fail_json(
|
||||
msg="The use of krbprincipalauthind '%s' is not supported "
|
||||
"by your IPA version" % "','".join(_invalid))
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors, exit_args,
|
||||
single_host):
|
||||
@@ -776,7 +785,8 @@ def main():
|
||||
default=None),
|
||||
auth_ind=dict(type='list', elements="str",
|
||||
aliases=["krbprincipalauthind"], default=None,
|
||||
choices=['radius', 'otp', 'pkinit', 'hardened', '']),
|
||||
choices=["radius", "otp", "pkinit", "hardened", "idp",
|
||||
""]),
|
||||
requires_pre_auth=dict(type="bool", aliases=["ipakrbrequirespreauth"],
|
||||
default=None),
|
||||
ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"],
|
||||
@@ -919,6 +929,8 @@ def main():
|
||||
|
||||
# Check version specific settings
|
||||
|
||||
check_authind(ansible_module, auth_ind)
|
||||
|
||||
server_realm = ansible_module.ipa_get_realm()
|
||||
|
||||
commands = []
|
||||
@@ -961,6 +973,7 @@ def main():
|
||||
sshpubkey = host.get("sshpubkey")
|
||||
userclass = host.get("userclass")
|
||||
auth_ind = host.get("auth_ind")
|
||||
check_authind(ansible_module, auth_ind)
|
||||
requires_pre_auth = host.get("requires_pre_auth")
|
||||
ok_as_delegate = host.get("ok_as_delegate")
|
||||
ok_to_auth_as_delegate = host.get("ok_to_auth_as_delegate")
|
||||
|
||||
354
plugins/modules/ipaidoverridegroup.py
Normal file
354
plugins/modules/ipaidoverridegroup.py
Normal file
@@ -0,0 +1,354 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2023 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, exither version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
# No rename support: 'ID overrides cannot be renamed'
|
||||
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipaidoverridegroup
|
||||
short_description: Manage FreeIPA idoverridegroup
|
||||
description: Manage FreeIPA idoverridegroups
|
||||
extends_documentation_fragment:
|
||||
- ipamodule_base_docs
|
||||
options:
|
||||
idview:
|
||||
description: The idoverridegroup idview string.
|
||||
type: str
|
||||
required: true
|
||||
aliases: ["idviewcn"]
|
||||
anchor:
|
||||
description: The list of anchors to override
|
||||
type: list
|
||||
elements: str
|
||||
required: true
|
||||
aliases: ["ipaanchoruuid"]
|
||||
description:
|
||||
description: Description
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["desc"]
|
||||
name:
|
||||
description: Group name
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["group_name", "cn"]
|
||||
gid:
|
||||
description: Group ID Number (int or "")
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["gidnumber"]
|
||||
fallback_to_ldap:
|
||||
description: |
|
||||
Allow falling back to AD DC LDAP when resolving AD trusted objects.
|
||||
For two-way trusts only.
|
||||
required: False
|
||||
type: bool
|
||||
delete_continue:
|
||||
description: |
|
||||
Continuous mode. Don't stop on errors.
|
||||
Valid only if `state` is `absent`.
|
||||
required: false
|
||||
type: bool
|
||||
aliases: ["continue"]
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
default: present
|
||||
type: str
|
||||
author:
|
||||
- Thomas Woerner (@t-woerner)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure test group test_group is present in idview test_idview
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
|
||||
# Ensure test group test_group is present in idview test_idview with
|
||||
# description
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
description: "test_group description"
|
||||
|
||||
# Ensure test group test_group is present in idview test_idview without
|
||||
# description
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
description: ""
|
||||
|
||||
# Ensure test group test_group is present in idview test_idview with internal
|
||||
# name test_123_group
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
name: test_123_group
|
||||
|
||||
# Ensure test group test_group is present in idview test_idview without
|
||||
# internal name
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
name: ""
|
||||
|
||||
# Ensure test group test_group is present in idview test_idview with gid 20001
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
gid: 20001
|
||||
|
||||
# Ensure test group test_group is present in idview test_idview without gid
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
gid: ""
|
||||
|
||||
# Ensure test group test_group is absent in idview test_idview
|
||||
- ipaidoverridegroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_group
|
||||
continue: true
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_idoverridegroup(module, idview, anchor):
|
||||
"""Find if a idoverridegroup with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idoverridegroup_show", idview,
|
||||
{"ipaanchoruuid": anchor,
|
||||
"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if idoverridegroup anchor is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(anchor, description, name, gid):
|
||||
# fallback_to_ldap is only a runtime tuning parameter
|
||||
_args = {}
|
||||
if anchor is not None:
|
||||
_args["ipaanchoruuid"] = anchor
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if name is not None:
|
||||
_args["cn"] = name
|
||||
if gid is not None:
|
||||
_args["gidnumber"] = gid
|
||||
return _args
|
||||
|
||||
|
||||
def gen_args_runtime(fallback_to_ldap):
|
||||
_args = {}
|
||||
if fallback_to_ldap is not None:
|
||||
_args["fallback_to_ldap"] = fallback_to_ldap
|
||||
return _args
|
||||
|
||||
|
||||
def merge_dicts(dict1, dict2):
|
||||
ret = dict1.copy()
|
||||
ret.update(dict2)
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
idview=dict(type="str", required=True, aliases=["idviewcn"]),
|
||||
anchor=dict(type="list", elements="str", required=True,
|
||||
aliases=["ipaanchoruuid"]),
|
||||
|
||||
# present
|
||||
description=dict(type="str", required=False, aliases=["desc"]),
|
||||
name=dict(type="str", required=False,
|
||||
aliases=["group_name", "cn"]),
|
||||
gid=dict(type="str", required=False, aliases=["gidnumber"]),
|
||||
|
||||
# runtime flags
|
||||
fallback_to_ldap=dict(type="bool", required=False),
|
||||
|
||||
# absent
|
||||
delete_continue=dict(type="bool", required=False,
|
||||
aliases=['continue'], default=None),
|
||||
|
||||
# No rename support: 'ID overrides cannot be renamed'
|
||||
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
|
||||
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
idview = ansible_module.params_get("idview")
|
||||
anchors = ansible_module.params_get("anchor")
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
name = ansible_module.params_get("name")
|
||||
gid = ansible_module.params_get("gid")
|
||||
|
||||
# runtime flags
|
||||
fallback_to_ldap = ansible_module.params_get("fallback_to_ldap")
|
||||
|
||||
# absent
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
invalid = []
|
||||
|
||||
if state == "present":
|
||||
if len(anchors) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one idoverridegroup can be added at a time.")
|
||||
invalid = ["delete_continue"]
|
||||
|
||||
if state == "absent":
|
||||
if len(anchors) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["description", "name", "gid"]
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state)
|
||||
|
||||
# Ensure parameter values are valid and have proper type.
|
||||
def int_or_empty_param(value, param):
|
||||
if value is not None and value != "":
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid value '%s' for argument '%s'" % (value, param)
|
||||
)
|
||||
return value
|
||||
|
||||
gid = int_or_empty_param(gid, "gid")
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
runtime_args = gen_args_runtime(fallback_to_ldap)
|
||||
commands = []
|
||||
for anchor in anchors:
|
||||
# Make sure idoverridegroup exists
|
||||
res_find = find_idoverridegroup(ansible_module, idview, anchor)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(anchor, description, name, gid)
|
||||
# fallback_to_ldap is only a runtime tuning parameter
|
||||
all_args = merge_dicts(args, runtime_args)
|
||||
|
||||
# Found the idoverridegroup
|
||||
if res_find is not None:
|
||||
# For idempotency: Remove empty sshpubkey list if
|
||||
# there are no sshpubkey in the found entry.
|
||||
if "ipasshpubkey" in args and \
|
||||
len(args["ipasshpubkey"]) < 1 and \
|
||||
"ipasshpubkey" not in res_find:
|
||||
del args["ipasshpubkey"]
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([idview, "idoverridegroup_mod",
|
||||
all_args])
|
||||
else:
|
||||
commands.append([idview, "idoverridegroup_add",
|
||||
all_args])
|
||||
|
||||
elif state == "absent":
|
||||
if res_find is not None:
|
||||
commands.append(
|
||||
[idview, "idoverridegroup_del",
|
||||
merge_dicts(
|
||||
{
|
||||
"ipaanchoruuid": anchor,
|
||||
"continue": delete_continue or False
|
||||
},
|
||||
runtime_args
|
||||
)]
|
||||
)
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
631
plugins/modules/ipaidoverrideuser.py
Normal file
631
plugins/modules/ipaidoverrideuser.py
Normal file
@@ -0,0 +1,631 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2023 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
# No rename support: 'ID overrides cannot be renamed'
|
||||
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipaidoverrideuser
|
||||
short_description: Manage FreeIPA idoverrideuser
|
||||
description: Manage FreeIPA idoverrideuser and idoverrideuser members
|
||||
extends_documentation_fragment:
|
||||
- ipamodule_base_docs
|
||||
options:
|
||||
idview:
|
||||
description: The idoverrideuser idview string.
|
||||
type: str
|
||||
required: true
|
||||
aliases: ["idviewcn"]
|
||||
anchor:
|
||||
description: The list of anchors to override
|
||||
type: list
|
||||
elements: str
|
||||
required: true
|
||||
aliases: ["ipaanchoruuid"]
|
||||
description:
|
||||
description: Description
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["desc"]
|
||||
name:
|
||||
description: The user (internally uid)
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["login"]
|
||||
uid:
|
||||
description: User ID Number (int or "")
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["uidnumber"]
|
||||
gecos:
|
||||
description: GECOS
|
||||
required: False
|
||||
type: str
|
||||
gidnumber:
|
||||
description: Group ID Number (int or "")
|
||||
required: False
|
||||
type: str
|
||||
homedir:
|
||||
description: Home directory
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["homedirectory"]
|
||||
shell:
|
||||
description: Login shell
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["loginshell"]
|
||||
sshpubkey:
|
||||
description: List of SSH public keys
|
||||
type: list
|
||||
element: str
|
||||
required: False
|
||||
aliases: ["ipasshpubkey"]
|
||||
certificate:
|
||||
description: List of Base-64 encoded user certificates
|
||||
type: list
|
||||
elements: str
|
||||
required: False
|
||||
aliases: ["usercertificate"]
|
||||
fallback_to_ldap:
|
||||
description: |
|
||||
Allow falling back to AD DC LDAP when resolving AD trusted objects.
|
||||
For two-way trusts only.
|
||||
required: False
|
||||
type: bool
|
||||
delete_continue:
|
||||
description: |
|
||||
Continuous mode. Don't stop on errors.
|
||||
Valid only if `state` is `absent`.
|
||||
required: false
|
||||
type: bool
|
||||
aliases: ["continue"]
|
||||
nomembers:
|
||||
description: |
|
||||
Suppress processing of membership attributes.
|
||||
Valid only if `state` is `absent`.
|
||||
type: str
|
||||
required: False
|
||||
aliases: ["no_members"]
|
||||
action:
|
||||
description: Work on idoverrideuser or member level.
|
||||
choices: ["idoverrideuser", "member"]
|
||||
default: idoverrideuser
|
||||
type: str
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
default: present
|
||||
type: str
|
||||
author:
|
||||
- Thomas Woerner (@t-woerner)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure test user test_user is present in idview test_idview
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with description
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
description: "test_user description"
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without
|
||||
# description
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
description: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with internal
|
||||
# name test_123_user
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
name: test_123_user
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without internal
|
||||
# name
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
name: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with uid 20001
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
uid: 20001
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without uid
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
uid: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with gecos
|
||||
# "Gecos Test"
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gecos: Gecos Test
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without gecos
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gecos: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with gidnumber
|
||||
# 20001
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gidnumber: 20001
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without
|
||||
# gidnumber
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
gidnumber: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with homedir
|
||||
# /Users
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
homedir: /Users
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without homedir
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
homedir: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with shell
|
||||
# /bin/someshell
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
shell: /bin/someshell
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without shell
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
shell: ""
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with sshpubkey
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
sshpubkey:
|
||||
- ssh-rsa AAAAB3NzaC1yc2EAAADAQABAAABgQCqmVDpEX5gnSjKuv97Ay ...
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without
|
||||
# sshpubkey
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
sshpubkey: []
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with 1
|
||||
# certificate
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview with 3
|
||||
# certificate members
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
|
||||
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
|
||||
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
|
||||
action: member
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without
|
||||
# 2 certificate members
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate:
|
||||
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
|
||||
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
# Ensure test user test_user is present in idview test_idview without
|
||||
# certificates
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
certificate: []
|
||||
|
||||
# Ensure test user test_user is absent in idview test_idview
|
||||
- ipaidoverrideuser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
idview: test_idview
|
||||
anchor: test_user
|
||||
continue: true
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list, encode_certificate
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_idoverrideuser(module, idview, anchor):
|
||||
"""Find if a idoverrideuser with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idoverrideuser_show", idview,
|
||||
{"ipaanchoruuid": anchor,
|
||||
"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if idoverrideuser anchor is not found.
|
||||
return None
|
||||
|
||||
_res = _result["result"]
|
||||
certs = _res.get("usercertificate")
|
||||
if certs is not None:
|
||||
_res["usercertificate"] = [encode_certificate(cert) for cert in certs]
|
||||
return _res
|
||||
|
||||
|
||||
def gen_args(anchor, description, name, uid, gecos, gidnumber, homedir, shell,
|
||||
sshpubkey):
|
||||
# fallback_to_ldap and nomembers are only runtime tuning parameters
|
||||
_args = {}
|
||||
if anchor is not None:
|
||||
_args["ipaanchoruuid"] = anchor
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if name is not None:
|
||||
_args["uid"] = name
|
||||
if uid is not None:
|
||||
_args["uidnumber"] = uid
|
||||
if gecos is not None:
|
||||
_args["gecos"] = gecos
|
||||
if gidnumber is not None:
|
||||
_args["gidnumber"] = gidnumber
|
||||
if homedir is not None:
|
||||
_args["homedirectory"] = homedir
|
||||
if shell is not None:
|
||||
_args["loginshell"] = shell
|
||||
if sshpubkey is not None:
|
||||
_args["ipasshpubkey"] = sshpubkey
|
||||
return _args
|
||||
|
||||
|
||||
def gen_args_runtime(fallback_to_ldap, nomembers):
|
||||
_args = {}
|
||||
if fallback_to_ldap is not None:
|
||||
_args["fallback_to_ldap"] = fallback_to_ldap
|
||||
if nomembers is not None:
|
||||
_args["no_members"] = nomembers
|
||||
return _args
|
||||
|
||||
|
||||
def gen_member_args(certificate):
|
||||
_args = {}
|
||||
if certificate is not None:
|
||||
_args["usercertificate"] = certificate
|
||||
return _args
|
||||
|
||||
|
||||
def merge_dicts(dict1, dict2):
|
||||
ret = dict1.copy()
|
||||
ret.update(dict2)
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
idview=dict(type="str", required=True, aliases=["idviewcn"]),
|
||||
anchor=dict(type="list", elements="str", required=True,
|
||||
aliases=["ipaanchoruuid"]),
|
||||
|
||||
# present
|
||||
description=dict(type="str", required=False, aliases=["desc"]),
|
||||
name=dict(type="str", required=False, aliases=["login"]),
|
||||
uid=dict(type="str", required=False, aliases=["uidnumber"]),
|
||||
gecos=dict(type="str", required=False),
|
||||
gidnumber=dict(type="str", required=False),
|
||||
homedir=dict(type="str", required=False,
|
||||
aliases=["homedirectory"]),
|
||||
shell=dict(type="str", required=False, aliases=["loginshell"]),
|
||||
sshpubkey=dict(type="list", elements="str", required=False,
|
||||
aliases=["ipasshpubkey"]),
|
||||
certificate=dict(type="list", elements="str", required=False,
|
||||
aliases=["usercertificate"]),
|
||||
fallback_to_ldap=dict(type="bool", required=False),
|
||||
nomembers=dict(type="bool", required=False,
|
||||
aliases=["no_members"]),
|
||||
|
||||
# absent
|
||||
delete_continue=dict(type="bool", required=False,
|
||||
aliases=['continue'], default=None),
|
||||
|
||||
# No rename support: 'ID overrides cannot be renamed'
|
||||
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
|
||||
|
||||
# action
|
||||
action=dict(type="str", default="idoverrideuser",
|
||||
choices=["member", "idoverrideuser"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
idview = ansible_module.params_get("idview")
|
||||
anchors = ansible_module.params_get("anchor")
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
name = ansible_module.params_get("name")
|
||||
uid = ansible_module.params_get("uid")
|
||||
gecos = ansible_module.params_get("gecos")
|
||||
gidnumber = ansible_module.params_get("gidnumber")
|
||||
homedir = ansible_module.params_get("homedir")
|
||||
shell = ansible_module.params_get("shell")
|
||||
sshpubkey = ansible_module.params_get("sshpubkey")
|
||||
certificate = ansible_module.params_get("certificate")
|
||||
fallback_to_ldap = ansible_module.params_get("fallback_to_ldap")
|
||||
nomembers = ansible_module.params_get("nomembers")
|
||||
action = ansible_module.params_get("action")
|
||||
|
||||
# absent
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
invalid = []
|
||||
|
||||
if state == "present":
|
||||
if len(anchors) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one idoverrideuser can be added at a time.")
|
||||
invalid = ["delete_continue"]
|
||||
if action == "member":
|
||||
invalid += ["description", "name", "uid", "gecos", "gidnumber",
|
||||
"homedir", "shell", "sshpubkey"]
|
||||
|
||||
if state == "absent":
|
||||
if len(anchors) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["description", "name", "uid", "gecos", "gidnumber",
|
||||
"homedir", "shell", "sshpubkey", "nomembers"]
|
||||
if action == "idoverrideuser":
|
||||
invalid += ["certificate"]
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
# Ensure parameter values are valid and have proper type.
|
||||
def int_or_empty_param(value, param):
|
||||
if value is not None and value != "":
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid value '%s' for argument '%s'" % (value, param)
|
||||
)
|
||||
return value
|
||||
|
||||
uid = int_or_empty_param(uid, "uid")
|
||||
gidnumber = int_or_empty_param(gidnumber, "gidnumber")
|
||||
|
||||
if certificate is not None:
|
||||
certificate = [cert.strip() for cert in certificate]
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
runtime_args = gen_args_runtime(fallback_to_ldap, nomembers)
|
||||
commands = []
|
||||
for anchor in anchors:
|
||||
# Make sure idoverrideuser exists
|
||||
res_find = find_idoverrideuser(ansible_module, idview, anchor)
|
||||
|
||||
# add/del lists
|
||||
certificate_add, certificate_del = [], []
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(anchor, description, name, uid, gecos,
|
||||
gidnumber, homedir, shell, sshpubkey)
|
||||
# fallback_to_ldap and nomembers are only runtime tuning
|
||||
# parameters
|
||||
all_args = merge_dicts(args, runtime_args)
|
||||
|
||||
if action == "idoverrideuser":
|
||||
# Found the idoverrideuser
|
||||
if res_find is not None:
|
||||
# For idempotency: Remove empty sshpubkey list if
|
||||
# there are no sshpubkey in the found entry.
|
||||
if "ipasshpubkey" in args and \
|
||||
len(args["ipasshpubkey"]) < 1 and \
|
||||
"ipasshpubkey" not in res_find:
|
||||
del args["ipasshpubkey"]
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([idview, "idoverrideuser_mod",
|
||||
all_args])
|
||||
else:
|
||||
commands.append([idview, "idoverrideuser_add",
|
||||
all_args])
|
||||
res_find = {}
|
||||
|
||||
member_args = gen_member_args(certificate)
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
|
||||
# Generate addition and removal lists
|
||||
certificate_add, certificate_del = gen_add_del_lists(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No idoverrideuser '%s' in idview '%s'" %
|
||||
(anchor, idview))
|
||||
|
||||
# Reduce add lists for certificate
|
||||
# to new entries only that are not in res_find.
|
||||
if certificate is not None:
|
||||
certificate_add = gen_add_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
elif state == "absent":
|
||||
if action == "idoverrideuser":
|
||||
if res_find is not None:
|
||||
commands.append(
|
||||
[idview, "idoverrideuser_del",
|
||||
merge_dicts(
|
||||
{
|
||||
"ipaanchoruuid": anchor,
|
||||
"continue": delete_continue or False
|
||||
},
|
||||
runtime_args
|
||||
)]
|
||||
)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No idoverrideuser '%s' in idview '%s'" %
|
||||
(anchor, idview))
|
||||
|
||||
# Reduce del lists of member_host and member_hostgroup,
|
||||
# to the entries only that are in res_find.
|
||||
if certificate is not None:
|
||||
certificate_del = gen_intersection_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Member management
|
||||
|
||||
# Add members
|
||||
if certificate_add:
|
||||
commands.append([idview, "idoverrideuser_add_cert",
|
||||
merge_dicts(
|
||||
{
|
||||
"ipaanchoruuid": anchor,
|
||||
"usercertificate": certificate_add
|
||||
},
|
||||
runtime_args
|
||||
)])
|
||||
|
||||
# Remove members
|
||||
|
||||
if certificate_del:
|
||||
commands.append([idview, "idoverrideuser_remove_cert",
|
||||
merge_dicts(
|
||||
{
|
||||
"ipaanchoruuid": anchor,
|
||||
"usercertificate": certificate_del
|
||||
},
|
||||
runtime_args
|
||||
)])
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
544
plugins/modules/ipaidp.py
Normal file
544
plugins/modules/ipaidp.py
Normal file
@@ -0,0 +1,544 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2023 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipaidp
|
||||
short_description: Manage FreeIPA idp
|
||||
description: Manage FreeIPA idp
|
||||
extends_documentation_fragment:
|
||||
- ipamodule_base_docs
|
||||
options:
|
||||
name:
|
||||
description: The list of idp name strings.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["cn"]
|
||||
auth_uri:
|
||||
description: OAuth 2.0 authorization endpoint
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpauthendpoint"]
|
||||
dev_auth_uri:
|
||||
description: Device authorization endpoint
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpdevauthendpoint"]
|
||||
token_uri:
|
||||
description: Token endpoint
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidptokenendpoint"]
|
||||
userinfo_uri:
|
||||
description: User information endpoint
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpuserinfoendpoint"]
|
||||
keys_uri:
|
||||
description: JWKS endpoint
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpkeysendpoint"]
|
||||
issuer_url:
|
||||
description: The Identity Provider OIDC URL
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpissuerurl"]
|
||||
client_id:
|
||||
description: OAuth 2.0 client identifier
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpclientid"]
|
||||
secret:
|
||||
description: OAuth 2.0 client secret
|
||||
required: false
|
||||
type: str
|
||||
no_log: true
|
||||
aliases: ["ipaidpclientsecret"]
|
||||
scope:
|
||||
description: OAuth 2.0 scope. Multiple scopes separated by space
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpscope"]
|
||||
idp_user_id:
|
||||
description: Attribute for user identity in OAuth 2.0 userinfo
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpsub"]
|
||||
provider:
|
||||
description: |
|
||||
Pre-defined template string. This provides the provider defaults, which
|
||||
can be overridden with the other IdP options.
|
||||
required: false
|
||||
type: str
|
||||
choices: ["google","github","microsoft","okta","keycloak"]
|
||||
aliases: ["ipaidpprovider"]
|
||||
organization:
|
||||
description: Organization ID or Realm name for IdP provider templates
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidporg"]
|
||||
base_url:
|
||||
description: Base URL for IdP provider templates
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["ipaidpbaseurl"]
|
||||
rename:
|
||||
description: |
|
||||
New name the Identity Provider server object. Only with state: renamed.
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["new_name"]
|
||||
delete_continue:
|
||||
description:
|
||||
Continuous mode. Don't stop on errors. Valid only if `state` is `absent`.
|
||||
required: false
|
||||
type: bool
|
||||
aliases: ["continue"]
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent", "renamed"]
|
||||
default: present
|
||||
type: str
|
||||
author:
|
||||
- Thomas Woerner (@t-woerner)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure keycloak idp my-keycloak-idp is present
|
||||
- ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-keycloak-idp
|
||||
provider: keycloak
|
||||
organization: main
|
||||
base_url: keycloak.idm.example.com:8443/auth
|
||||
client_id: my-client-id
|
||||
|
||||
# Ensure google idp my-google-idp is present
|
||||
- ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-google-idp
|
||||
auth_uri: https://accounts.google.com/o/oauth2/auth
|
||||
dev_auth_uri: https://oauth2.googleapis.com/device/code
|
||||
token_uri: https://oauth2.googleapis.com/token
|
||||
userinfo_uri: https://openidconnect.googleapis.com/v1/userinfo
|
||||
client_id: my-client-id
|
||||
scope: "openid email"
|
||||
idp_user_id: email
|
||||
|
||||
# Ensure google idp my-google-idp is present without using provider
|
||||
- ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-google-idp
|
||||
provider: google
|
||||
client_id: my-google-client-id
|
||||
|
||||
# Ensure keycloak idp my-keycloak-idp is absent
|
||||
- ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-keycloak-idp
|
||||
delete_continue: true
|
||||
state: absent
|
||||
|
||||
# Ensure idps my-keycloak-idp, my-github-idp and my-google-idp are absent
|
||||
- ipaidp:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name:
|
||||
- my-keycloak-idp
|
||||
- my-github-idp
|
||||
- my-google-idp
|
||||
delete_continue: true
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, template_str
|
||||
from ansible.module_utils import six
|
||||
from copy import deepcopy
|
||||
import string
|
||||
from itertools import chain
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
# Copy from FreeIPA ipaserver/plugins/idp.py
|
||||
idp_providers = {
|
||||
'google': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://accounts.google.com/o/oauth2/auth',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://oauth2.googleapis.com/device/code',
|
||||
'ipaidptokenendpoint':
|
||||
'https://oauth2.googleapis.com/token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://openidconnect.googleapis.com/v1/userinfo',
|
||||
'ipaidpkeysendpoint':
|
||||
'https://www.googleapis.com/oauth2/v3/certs',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email'},
|
||||
'github': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://github.com/login/oauth/authorize',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://github.com/login/device/code',
|
||||
'ipaidptokenendpoint':
|
||||
'https://github.com/login/oauth/access_token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://api.github.com/user',
|
||||
'ipaidpscope': 'user',
|
||||
'ipaidpsub': 'login'},
|
||||
'microsoft': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/'
|
||||
'authorize',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/'
|
||||
'devicecode',
|
||||
'ipaidptokenendpoint':
|
||||
'https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/'
|
||||
'token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://graph.microsoft.com/oidc/userinfo',
|
||||
'ipaidpkeysendpoint':
|
||||
'https://login.microsoftonline.com/common/discovery/v2.0/keys',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email',
|
||||
},
|
||||
'okta': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/authorize',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/device/authorize',
|
||||
'ipaidptokenendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/userinfo',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email'},
|
||||
'keycloak': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/auth',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/auth/device',
|
||||
'ipaidptokenendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/userinfo',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email'},
|
||||
}
|
||||
|
||||
|
||||
def find_idp(module, name):
|
||||
"""Find if a idp with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idp_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if idp name is not found.
|
||||
return None
|
||||
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(auth_uri, dev_auth_uri, token_uri, userinfo_uri, keys_uri,
|
||||
issuer_url, client_id, secret, scope, idp_user_id, organization,
|
||||
base_url):
|
||||
_args = {}
|
||||
if auth_uri is not None:
|
||||
_args["ipaidpauthendpoint"] = auth_uri
|
||||
if dev_auth_uri is not None:
|
||||
_args["ipaidpdevauthendpoint"] = dev_auth_uri
|
||||
if token_uri is not None:
|
||||
_args["ipaidptokenendpoint"] = token_uri
|
||||
if userinfo_uri is not None:
|
||||
_args["ipaidpuserinfoendpoint"] = userinfo_uri
|
||||
if keys_uri is not None:
|
||||
_args["ipaidpkeysendpoint"] = keys_uri
|
||||
if issuer_url is not None:
|
||||
_args["ipaidpissuerurl"] = issuer_url
|
||||
if client_id is not None:
|
||||
_args["ipaidpclientid"] = client_id
|
||||
if secret is not None:
|
||||
_args["ipaidpclientsecret"] = secret
|
||||
if scope is not None:
|
||||
_args["ipaidpscope"] = scope
|
||||
if idp_user_id is not None:
|
||||
_args["ipaidpsub"] = idp_user_id
|
||||
if organization is not None:
|
||||
_args["ipaidporg"] = organization
|
||||
if base_url is not None:
|
||||
_args["ipaidpbaseurl"] = base_url
|
||||
return _args
|
||||
|
||||
|
||||
# Copied and adapted from FreeIPA ipaserver/plugins/idp.py
|
||||
def convert_provider_to_endpoints(module, _args, provider):
|
||||
"""Convert provider option to auth-uri and token-uri,.."""
|
||||
if provider not in idp_providers:
|
||||
module.fail_json(msg="Provider '%s' is unknown" % provider)
|
||||
|
||||
# For each string in the template check if a variable
|
||||
# is required, it is provided as an option
|
||||
points = deepcopy(idp_providers[provider])
|
||||
_r = string.Template.pattern
|
||||
for (_k, _v) in points.items():
|
||||
# build list of variables to be replaced
|
||||
subs = list(chain.from_iterable(
|
||||
(filter(None, _s) for _s in _r.findall(_v))))
|
||||
if subs:
|
||||
for _s in subs:
|
||||
if _s not in _args:
|
||||
module.fail_json(msg="Parameter '%s' is missing" % _s)
|
||||
points[_k] = template_str(_v, _args)
|
||||
elif _k in _args:
|
||||
points[_k] = _args[_k]
|
||||
|
||||
_args.update(points)
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
name=dict(type="list", elements="str", required=True,
|
||||
aliases=["cn"]),
|
||||
# present
|
||||
auth_uri=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpauthendpoint"]),
|
||||
dev_auth_uri=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpdevauthendpoint"]),
|
||||
token_uri=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidptokenendpoint"]),
|
||||
userinfo_uri=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpuserinfoendpoint"]),
|
||||
keys_uri=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpkeysendpoint"]),
|
||||
issuer_url=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpissuerurl"]),
|
||||
client_id=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpclientid"]),
|
||||
secret=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpclientsecret"], no_log=True),
|
||||
scope=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpscope"]),
|
||||
idp_user_id=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpsub"]),
|
||||
provider=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpprovider"],
|
||||
choices=["google", "github", "microsoft", "okta",
|
||||
"keycloak"]),
|
||||
organization=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidporg"]),
|
||||
base_url=dict(required=False, type="str", default=None,
|
||||
aliases=["ipaidpbaseurl"]),
|
||||
rename=dict(required=False, type="str", default=None,
|
||||
aliases=["new_name"]),
|
||||
delete_continue=dict(required=False, type="bool", default=None,
|
||||
aliases=['continue']),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "renamed"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
# mutually_exclusive=[],
|
||||
# required_one_of=[]
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
names = ansible_module.params_get("name")
|
||||
|
||||
# present
|
||||
auth_uri = ansible_module.params_get("auth_uri")
|
||||
dev_auth_uri = ansible_module.params_get("dev_auth_uri")
|
||||
token_uri = ansible_module.params_get("token_uri")
|
||||
userinfo_uri = ansible_module.params_get("userinfo_uri")
|
||||
keys_uri = ansible_module.params_get("keys_uri")
|
||||
issuer_url = ansible_module.params_get("issuer_url")
|
||||
client_id = ansible_module.params_get("client_id")
|
||||
secret = ansible_module.params_get("secret")
|
||||
scope = ansible_module.params_get("scope")
|
||||
idp_user_id = ansible_module.params_get("idp_user_id")
|
||||
provider = ansible_module.params_get("provider")
|
||||
organization = ansible_module.params_get("organization")
|
||||
base_url = ansible_module.params_get("base_url")
|
||||
rename = ansible_module.params_get("rename")
|
||||
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
invalid = []
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one idp can be added at a time.")
|
||||
if provider:
|
||||
if any([auth_uri, dev_auth_uri, token_uri, userinfo_uri,
|
||||
keys_uri]):
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot specify both individual endpoints and IdP "
|
||||
"provider")
|
||||
if provider not in idp_providers:
|
||||
ansible_module.fail_json(
|
||||
msg="Provider '%s' is unknown" % provider)
|
||||
else:
|
||||
if not auth_uri:
|
||||
ansible_module.fail_json(
|
||||
msg="Parameter '%s' is missing" % "auth_uri")
|
||||
if not dev_auth_uri:
|
||||
ansible_module.fail_json(
|
||||
msg="Parameter '%s' is missing" % "dev_auth_uri")
|
||||
if not token_uri:
|
||||
ansible_module.fail_json(
|
||||
msg="Parameter '%s' is missing" % "token_uri")
|
||||
if not userinfo_uri:
|
||||
ansible_module.fail_json(
|
||||
msg="Parameter '%s' is missing" % "userinfo_uri")
|
||||
invalid = ["rename", "delete_continue"]
|
||||
else:
|
||||
# state renamed and absent
|
||||
invalid = ["auth_uri", "dev_auth_uri", "token_uri", "userinfo_uri",
|
||||
"keys_uri", "issuer_url", "client_id", "secret", "scope",
|
||||
"idp_user_id", "provider", "organization", "base_url"]
|
||||
|
||||
if state == "renamed":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one permission can be renamed at a time.")
|
||||
invalid += ["delete_continue"]
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid += ["rename"]
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state)
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
if not ansible_module.ipa_command_exists("idp_add"):
|
||||
ansible_module.fail_json(
|
||||
msg="Managing idp is not supported by your IPA version")
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure idp exists
|
||||
res_find = find_idp(ansible_module, name)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(auth_uri, dev_auth_uri, token_uri,
|
||||
userinfo_uri, keys_uri, issuer_url, client_id,
|
||||
secret, scope, idp_user_id, organization,
|
||||
base_url)
|
||||
|
||||
if provider is not None:
|
||||
convert_provider_to_endpoints(ansible_module, args,
|
||||
provider)
|
||||
|
||||
# Found the idp
|
||||
if res_find is not None:
|
||||
# The parameters ipaidpprovider, ipaidporg and
|
||||
# ipaidpbaseurl are only available for idp-add to create
|
||||
# then endpoints using provider, Therefore we have to
|
||||
# remove them from args.
|
||||
for arg in ["ipaidpprovider", "ipaidporg",
|
||||
"ipaidpbaseurl"]:
|
||||
if arg in args:
|
||||
del args[arg]
|
||||
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "idp_mod", args])
|
||||
else:
|
||||
commands.append([name, "idp_add", args])
|
||||
|
||||
elif state == "absent":
|
||||
if res_find is not None:
|
||||
_args = {}
|
||||
if delete_continue is not None:
|
||||
_args = {"continue": delete_continue}
|
||||
commands.append([name, "idp_del", _args])
|
||||
|
||||
elif state == "renamed":
|
||||
if not rename:
|
||||
ansible_module.fail_json(msg="No rename value given.")
|
||||
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No idp found to be renamed: '%s'" % (name))
|
||||
|
||||
if name != rename:
|
||||
commands.append(
|
||||
[name, "idp_mod", {"rename": rename}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -157,8 +157,7 @@ def find_idrange(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if idrange name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(
|
||||
|
||||
362
plugins/modules/ipaidview.py
Normal file
362
plugins/modules/ipaidview.py
Normal file
@@ -0,0 +1,362 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2023 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipaidview
|
||||
short_description: Manage FreeIPA idview
|
||||
description: Manage FreeIPA idview and idview host members
|
||||
extends_documentation_fragment:
|
||||
- ipamodule_base_docs
|
||||
options:
|
||||
name:
|
||||
description: The list of idview name strings.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["cn"]
|
||||
description:
|
||||
description: Description
|
||||
required: False
|
||||
type: str
|
||||
aliases: ["desc"]
|
||||
domain_resolution_order:
|
||||
description: |
|
||||
Colon-separated list of domains used for short name qualification
|
||||
required: False
|
||||
type: str
|
||||
aliases: ["ipadomainresolutionorder"]
|
||||
host:
|
||||
description: Hosts to apply the ID View to
|
||||
required: False
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["hosts"]
|
||||
rename:
|
||||
description: Rename the ID view object
|
||||
required: False
|
||||
type: str
|
||||
aliases: ["new_name"]
|
||||
delete_continue:
|
||||
description: |
|
||||
Continuous mode. Don't stop on errors.
|
||||
Valid only if `state` is `absent`.
|
||||
required: false
|
||||
type: bool
|
||||
aliases: ["continue"]
|
||||
action:
|
||||
description: Work on idview or member level.
|
||||
choices: ["idview", "member"]
|
||||
default: idview
|
||||
type: str
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent", "renamed"]
|
||||
default: present
|
||||
type: str
|
||||
author:
|
||||
- Thomas Woerner (@t-woerner)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure idview test_idview is present
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
|
||||
# name: Ensure host testhost.example.com is applied to idview test_idview
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
host: testhost.example.com
|
||||
action: member
|
||||
|
||||
# Ensure host testhost.example.com is not applied to idview test_idview
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
host: testhost.example.com
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
# Ensure idview "test_idview" is present with domain_resolution_order for
|
||||
# "ad.example.com:ipa.example.com"
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
domain_resolution_order: "ad.example.com:ipa.example.com"
|
||||
|
||||
# Ensure idview test_idview is absent
|
||||
- ipaidview:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: test_idview
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_idview(module, name):
|
||||
"""Find if a idview with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idview_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if idview name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def valid_host(module, name):
|
||||
try:
|
||||
module.ipa_command("host_show", name, {})
|
||||
except ipalib_errors.NotFound:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def gen_args(description, domain_resolution_order):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if domain_resolution_order is not None:
|
||||
_args["ipadomainresolutionorder"] = domain_resolution_order
|
||||
return _args
|
||||
|
||||
|
||||
def gen_member_args(host):
|
||||
_args = {}
|
||||
if host is not None:
|
||||
_args["host"] = host
|
||||
return _args
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
name=dict(type="list", elements="str", required=True,
|
||||
aliases=["cn"]),
|
||||
# present
|
||||
description=dict(type="str", required=False, aliases=["desc"]),
|
||||
domain_resolution_order=dict(type="str", required=False,
|
||||
aliases=["ipadomainresolutionorder"]),
|
||||
host=dict(type="list", elements="str", required=False,
|
||||
aliases=["hosts"], default=None),
|
||||
rename=dict(type="str", required=False, aliases=["new_name"]),
|
||||
delete_continue=dict(type="bool", required=False,
|
||||
aliases=['continue'], default=None),
|
||||
# action
|
||||
action=dict(type="str", default="idview",
|
||||
choices=["member", "idview"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "renamed"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
names = ansible_module.params_get("name")
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
domain_resolution_order = ansible_module.params_get(
|
||||
"domain_resolution_order")
|
||||
host = ansible_module.params_get("host")
|
||||
rename = ansible_module.params_get("rename")
|
||||
|
||||
action = ansible_module.params_get("action")
|
||||
|
||||
# absent
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
invalid = []
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one idview can be added at a time.")
|
||||
invalid = ["delete_continue", "rename"]
|
||||
if action == "member":
|
||||
invalid += ["description", "domain_resolution_order"]
|
||||
|
||||
if state == "renamed":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one idoverridegroup can be renamed at a time.")
|
||||
if not rename:
|
||||
ansible_module.fail_json(
|
||||
msg="Rename is required for state: renamed.")
|
||||
if action == "member":
|
||||
ansible_module.fail_json(
|
||||
msg="Action member can not be used with state: renamed.")
|
||||
invalid = ["description", "domain_resolution_order", "host",
|
||||
"delete_continue"]
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["description", "domain_resolution_order", "rename"]
|
||||
if action == "idview":
|
||||
invalid += ["host"]
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure idview exists
|
||||
res_find = find_idview(ansible_module, name)
|
||||
|
||||
# add/del lists
|
||||
host_add, host_del = [], []
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(description, domain_resolution_order)
|
||||
|
||||
if action == "idview":
|
||||
# Found the idview
|
||||
if res_find is not None:
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "idview_mod", args])
|
||||
else:
|
||||
commands.append([name, "idview_add", args])
|
||||
res_find = {}
|
||||
|
||||
member_args = gen_member_args(host)
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
|
||||
# Generate addition and removal lists
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get("appliedtohosts"))
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No idview '%s'" % name)
|
||||
|
||||
# Reduce add lists for host
|
||||
# to new entries only that are not in res_find.
|
||||
if host is not None:
|
||||
host_add = gen_add_list(
|
||||
host, res_find.get("appliedtohosts"))
|
||||
|
||||
elif state == "absent":
|
||||
if action == "idview":
|
||||
if res_find is not None:
|
||||
commands.append(
|
||||
[name, "idview_del",
|
||||
{"continue": delete_continue or False}]
|
||||
)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No idview '%s'" % name)
|
||||
|
||||
# Reduce del lists of member_host
|
||||
# to the entries only that are in res_find.
|
||||
if host is not None:
|
||||
host_del = gen_intersection_list(
|
||||
host, res_find.get("appliedtohosts"))
|
||||
|
||||
elif state == "renamed":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No idview '%s'" % name)
|
||||
else:
|
||||
commands.append([name, 'idview_mod', {"rename": rename}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Member management
|
||||
|
||||
# Add members
|
||||
if host_add:
|
||||
for host in host_add:
|
||||
if not valid_host(ansible_module, host):
|
||||
ansible_module.fail_json("Invalid host '%s'" % host)
|
||||
commands.append([name, "idview_apply", {"host": host_add}])
|
||||
|
||||
# Remove members
|
||||
if host_del:
|
||||
# idview_unapply does not have the idview name (cn) as an arg.
|
||||
# It is removing the host from any idview it is applied to.
|
||||
# But as we create the intersection with the list of hosts of
|
||||
# the idview, we emulate the correct behaviour. But this means
|
||||
# that there is no general idview_unapply like in the cli.
|
||||
commands.append([None, "idview_unapply", {"host": host_del}])
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -86,8 +86,7 @@ def find_location(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if location name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(description):
|
||||
|
||||
@@ -164,8 +164,7 @@ def find_permission(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if permission name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(right, attrs, bindtype, subtree,
|
||||
|
||||
@@ -138,8 +138,7 @@ def find_privilege(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if privilege name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -45,82 +45,84 @@ options:
|
||||
required: false
|
||||
aliases: ["cn"]
|
||||
maxlife:
|
||||
description: Maximum password lifetime (in days)
|
||||
description: Maximum password lifetime (in days). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbmaxpwdlife"]
|
||||
minlife:
|
||||
description: Minimum password lifetime (in hours)
|
||||
description: Minimum password lifetime (in hours). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbminpwdlife"]
|
||||
history:
|
||||
description: Password history size
|
||||
description: Password history size. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdhistorylength"]
|
||||
minclasses:
|
||||
description: Minimum number of character classes
|
||||
description: Minimum number of character classes. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdmindiffchars"]
|
||||
minlength:
|
||||
description: Minimum length of password
|
||||
description: Minimum length of password. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdminlength"]
|
||||
priority:
|
||||
description: Priority of the policy (higher number means lower priority)
|
||||
description: >
|
||||
Priority of the policy (higher number means lower priority). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["cospriority"]
|
||||
maxfail:
|
||||
description: Consecutive failures before lockout
|
||||
description: Consecutive failures before lockout. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdmaxfailure"]
|
||||
failinterval:
|
||||
description: Period after which failure count will be reset (seconds)
|
||||
description: >
|
||||
Period after which failure count will be reset (seconds). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdfailurecountinterval"]
|
||||
lockouttime:
|
||||
description: Period for which lockout is enforced (seconds)
|
||||
description: Period for which lockout is enforced (seconds). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdlockoutduration"]
|
||||
maxrepeat:
|
||||
description: >
|
||||
Maximum number of same consecutive characters.
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdmaxrepeat"]
|
||||
maxsequence:
|
||||
description: >
|
||||
The maximum length of monotonic character sequences (abcd).
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdmaxsequence"]
|
||||
dictcheck:
|
||||
description: >
|
||||
Check if the password is a dictionary word.
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (bool or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdictcheck"]
|
||||
usercheck:
|
||||
description: >
|
||||
Check if the password contains the username.
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (bool or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdusercheck"]
|
||||
gracelimit:
|
||||
description: >
|
||||
Number of LDAP authentications allowed after expiration.
|
||||
Requires IPA 4.10.1+
|
||||
Requires IPA 4.10.1+. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["passwordgracelimit"]
|
||||
@@ -151,7 +153,7 @@ RETURN = """
|
||||
"""
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, boolean
|
||||
|
||||
|
||||
def find_pwpolicy(module, name):
|
||||
@@ -359,17 +361,12 @@ def main():
|
||||
gracelimit = int_or_empty_param(gracelimit, "gracelimit")
|
||||
|
||||
def bool_or_empty_param(value, param): # pylint: disable=R1710
|
||||
# As of Ansible 2.14, values True, False, Yes an No, with variable
|
||||
# capitalization are accepted by Ansible.
|
||||
if not value:
|
||||
if value is None or value == "":
|
||||
return value
|
||||
if value in ["TRUE", "True", "true", "YES", "Yes", "yes"]:
|
||||
return True
|
||||
if value in ["FALSE", "False", "false", "NO", "No", "no"]:
|
||||
return False
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid value '%s' for argument '%s'." % (value, param)
|
||||
)
|
||||
try:
|
||||
return boolean(value)
|
||||
except TypeError as terr:
|
||||
ansible_module.fail_json(msg="Param '%s': %s" % (param, str(terr)))
|
||||
|
||||
dictcheck = bool_or_empty_param(dictcheck, "dictcheck")
|
||||
usercheck = bool_or_empty_param(usercheck, "usercheck")
|
||||
|
||||
@@ -143,8 +143,7 @@ def find_role(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if role name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(module):
|
||||
|
||||
@@ -123,8 +123,7 @@ def find_selfservice(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if selfservice name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(permission, attribute):
|
||||
|
||||
@@ -45,9 +45,9 @@ options:
|
||||
aliases: ["cn"]
|
||||
location:
|
||||
description: |
|
||||
The server location string.
|
||||
"" for location reset.
|
||||
Only in state: present.
|
||||
The server DNS location.
|
||||
Only available with 'state: present'.
|
||||
Use "" for location reset.
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipalocation_location"]
|
||||
@@ -55,46 +55,46 @@ options:
|
||||
description: |
|
||||
Weight for server services
|
||||
Values 0 to 65535, -1 for weight reset.
|
||||
Only in state: present.
|
||||
Only available with 'state: present'.
|
||||
required: false
|
||||
type: int
|
||||
aliases: ["ipaserviceweight"]
|
||||
hidden:
|
||||
description: |
|
||||
Set hidden state of a server.
|
||||
Only in state: present.
|
||||
Only available with 'state: present'.
|
||||
required: false
|
||||
type: bool
|
||||
no_members:
|
||||
description: |
|
||||
Suppress processing of membership attributes
|
||||
Only in state: present.
|
||||
Only available with 'state: present'.
|
||||
required: false
|
||||
type: bool
|
||||
delete_continue:
|
||||
description: |
|
||||
Continuous mode: Don't stop on errors.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
aliases: ["continue"]
|
||||
ignore_last_of_role:
|
||||
description: |
|
||||
Skip a check whether the last CA master or DNS server is removed.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
ignore_topology_disconnect:
|
||||
description: |
|
||||
Ignore topology connectivity problems after removal.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
force:
|
||||
description: |
|
||||
Force server removal even if it does not exist.
|
||||
Will always result in changed.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
state:
|
||||
@@ -202,8 +202,7 @@ def find_server(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if server name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def server_role_status(module, name):
|
||||
@@ -218,8 +217,7 @@ def server_role_status(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if server name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"][0]
|
||||
return _result["result"][0]
|
||||
|
||||
|
||||
def gen_args(location, service_weight, no_members, delete_continue,
|
||||
|
||||
@@ -74,7 +74,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
choices: ["otp", "radius", "pkinit", "hardened", ""]
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", ""]
|
||||
aliases: ["krbprincipalauthind"]
|
||||
skip_host_check:
|
||||
description: Skip checking if host object exists.
|
||||
@@ -185,7 +185,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
choices: ["otp", "radius", "pkinit", "hardened", ""]
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", ""]
|
||||
aliases: ["krbprincipalauthind"]
|
||||
skip_host_check:
|
||||
description: Skip checking if host object exists.
|
||||
@@ -414,15 +414,15 @@ def gen_args(pac_type, auth_ind, skip_host_check, force, requires_pre_auth,
|
||||
if auth_ind is not None:
|
||||
_args['krbprincipalauthind'] = auth_ind
|
||||
if skip_host_check is not None:
|
||||
_args['skip_host_check'] = (skip_host_check)
|
||||
_args['skip_host_check'] = skip_host_check
|
||||
if force is not None:
|
||||
_args['force'] = (force)
|
||||
_args['force'] = force
|
||||
if requires_pre_auth is not None:
|
||||
_args['ipakrbrequirespreauth'] = (requires_pre_auth)
|
||||
_args['ipakrbrequirespreauth'] = requires_pre_auth
|
||||
if ok_as_delegate is not None:
|
||||
_args['ipakrbokasdelegate'] = (ok_as_delegate)
|
||||
_args['ipakrbokasdelegate'] = ok_as_delegate
|
||||
if ok_to_auth_as_delegate is not None:
|
||||
_args['ipakrboktoauthasdelegate'] = (ok_to_auth_as_delegate)
|
||||
_args['ipakrboktoauthasdelegate'] = ok_to_auth_as_delegate
|
||||
|
||||
return _args
|
||||
|
||||
@@ -433,9 +433,9 @@ def gen_args_smb(netbiosname, ok_as_delegate, ok_to_auth_as_delegate):
|
||||
if netbiosname is not None:
|
||||
_args['ipantflatname'] = netbiosname
|
||||
if ok_as_delegate is not None:
|
||||
_args['ipakrbokasdelegate'] = (ok_as_delegate)
|
||||
_args['ipakrbokasdelegate'] = ok_as_delegate
|
||||
if ok_to_auth_as_delegate is not None:
|
||||
_args['ipakrboktoauthasdelegate'] = (ok_to_auth_as_delegate)
|
||||
_args['ipakrboktoauthasdelegate'] = ok_to_auth_as_delegate
|
||||
|
||||
return _args
|
||||
|
||||
@@ -491,6 +491,15 @@ def check_parameters(module, state, action, names):
|
||||
module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
|
||||
def check_authind(module, auth_ind):
|
||||
_invalid = module.ipa_command_invalid_param_choices(
|
||||
"service_add", "krbprincipalauthind", auth_ind)
|
||||
if _invalid:
|
||||
module.fail_json(
|
||||
msg="The use of krbprincipalauthind '%s' is not supported "
|
||||
"by your IPA version" % "','".join(_invalid))
|
||||
|
||||
|
||||
def init_ansible_module():
|
||||
service_spec = dict(
|
||||
# service attributesstr
|
||||
@@ -506,7 +515,8 @@ def init_ansible_module():
|
||||
choices=["MS-PAC", "PAD", "NONE", ""]),
|
||||
auth_ind=dict(type="list", elements="str",
|
||||
aliases=["krbprincipalauthind"],
|
||||
choices=["otp", "radius", "pkinit", "hardened", ""]),
|
||||
choices=["otp", "radius", "pkinit", "hardened", "idp",
|
||||
""]),
|
||||
skip_host_check=dict(type="bool"),
|
||||
force=dict(type="bool"),
|
||||
requires_pre_auth=dict(
|
||||
@@ -642,6 +652,7 @@ def main():
|
||||
if skip_host_check and not has_skip_host_check:
|
||||
ansible_module.fail_json(
|
||||
msg="Skipping host check is not supported by your IPA version")
|
||||
check_authind(ansible_module, auth_ind)
|
||||
|
||||
commands = []
|
||||
keytab_members = ["user", "group", "host", "hostgroup"]
|
||||
@@ -664,6 +675,7 @@ def main():
|
||||
certificate = [cert.strip() for cert in certificate]
|
||||
pac_type = service.get("pac_type")
|
||||
auth_ind = service.get("auth_ind")
|
||||
check_authind(ansible_module, auth_ind)
|
||||
skip_host_check = service.get("skip_host_check")
|
||||
if skip_host_check and not has_skip_host_check:
|
||||
ansible_module.fail_json(
|
||||
|
||||
@@ -145,8 +145,7 @@ def find_servicedelegationrule(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if servicedelegationrule name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def check_targets(module, targets):
|
||||
|
||||
@@ -121,8 +121,7 @@ def find_servicedelegationtarget(module, name):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if servicedelegationtarget name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -122,8 +122,7 @@ def find_sudocmdgroup(module, name):
|
||||
_result = module.ipa_command("sudocmdgroup_show", name, args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(description, nomembers):
|
||||
|
||||
@@ -80,6 +80,10 @@ options:
|
||||
description: The home directory
|
||||
type: str
|
||||
required: false
|
||||
gecos:
|
||||
description: The GECOS
|
||||
type: str
|
||||
required: false
|
||||
shell:
|
||||
description: The login shell
|
||||
type: str
|
||||
@@ -133,6 +137,10 @@ options:
|
||||
type: int
|
||||
required: false
|
||||
aliases: ["gidnumber"]
|
||||
street:
|
||||
description: Street address
|
||||
type: str
|
||||
required: false
|
||||
city:
|
||||
description: City
|
||||
type: str
|
||||
@@ -200,7 +208,7 @@ options:
|
||||
Use empty string to reset userauthtype to the initial value.
|
||||
type: list
|
||||
elements: str
|
||||
choices: ['password', 'radius', 'otp', '']
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
required: false
|
||||
aliases: ["ipauserauthtype"]
|
||||
userclass:
|
||||
@@ -234,10 +242,45 @@ options:
|
||||
description: Employee Type
|
||||
type: str
|
||||
required: false
|
||||
smb_logon_script:
|
||||
description: SMB logon script path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantlogonscript"]
|
||||
smb_profile_path:
|
||||
description: SMB profile path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantprofilepath"]
|
||||
smb_home_dir:
|
||||
description: SMB Home Directory
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipanthomedirectory"]
|
||||
smb_home_drive:
|
||||
description: SMB Home Directory Drive
|
||||
type: str
|
||||
required: false
|
||||
choices: [
|
||||
'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:',
|
||||
'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:',
|
||||
'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:', ''
|
||||
]
|
||||
aliases: ["ipanthomedirectorydrive"]
|
||||
preferredlanguage:
|
||||
description: Preferred Language
|
||||
type: str
|
||||
required: false
|
||||
idp:
|
||||
description: External IdP configuration
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpconfiglink"]
|
||||
idp_user_id:
|
||||
description: A string that identifies the user at external IdP
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpsub"]
|
||||
certificate:
|
||||
description: List of base-64 encoded user certificates
|
||||
type: list
|
||||
@@ -304,6 +347,10 @@ options:
|
||||
description: The home directory
|
||||
type: str
|
||||
required: false
|
||||
gecos:
|
||||
description: The GECOS
|
||||
type: str
|
||||
required: false
|
||||
shell:
|
||||
description: The login shell
|
||||
type: str
|
||||
@@ -357,6 +404,10 @@ options:
|
||||
type: int
|
||||
required: false
|
||||
aliases: ["gidnumber"]
|
||||
street:
|
||||
description: Street address
|
||||
type: str
|
||||
required: false
|
||||
city:
|
||||
description: City
|
||||
type: str
|
||||
@@ -424,7 +475,7 @@ options:
|
||||
Use empty string to reset userauthtype to the initial value.
|
||||
type: list
|
||||
elements: str
|
||||
choices: ['password', 'radius', 'otp', '']
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
required: false
|
||||
aliases: ["ipauserauthtype"]
|
||||
userclass:
|
||||
@@ -458,10 +509,45 @@ options:
|
||||
description: Employee Type
|
||||
type: str
|
||||
required: false
|
||||
smb_logon_script:
|
||||
description: SMB logon script path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantlogonscript"]
|
||||
smb_profile_path:
|
||||
description: SMB profile path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantprofilepath"]
|
||||
smb_home_dir:
|
||||
description: SMB Home Directory
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipanthomedirectory"]
|
||||
smb_home_drive:
|
||||
description: SMB Home Directory Drive
|
||||
type: str
|
||||
required: false
|
||||
choices: [
|
||||
'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:',
|
||||
'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:',
|
||||
'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:', ''
|
||||
]
|
||||
aliases: ["ipanthomedirectorydrive"]
|
||||
preferredlanguage:
|
||||
description: Preferred Language
|
||||
type: str
|
||||
required: false
|
||||
idp:
|
||||
description: External IdP configuration
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpconfiglink"]
|
||||
idp_user_id:
|
||||
description: A string that identifies the user at external IdP
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpsub"]
|
||||
certificate:
|
||||
description: List of base-64 encoded user certificates
|
||||
type: list
|
||||
@@ -597,6 +683,17 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: pinky,brain
|
||||
state: disabled
|
||||
|
||||
# Ensure a user has SMB attributes
|
||||
- ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: smbuser
|
||||
first: SMB
|
||||
last: User
|
||||
smb_logon_script: N:\\logonscripts\\startup
|
||||
smb_profile_path: \\\\server\\profiles\\some_profile
|
||||
smb_home_dir: \\\\users\\home\\smbuser
|
||||
smb_home_drive: "U:"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
@@ -652,12 +749,14 @@ def find_user(module, name):
|
||||
return _result
|
||||
|
||||
|
||||
def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
email, principalexpiration, passwordexpiration, password,
|
||||
random, uid, gid, city, userstate, postalcode, phone, mobile,
|
||||
pager, fax, orgunit, title, carlicense, sshpubkey, userauthtype,
|
||||
userclass, radius, radiususer, departmentnumber, employeenumber,
|
||||
employeetype, preferredlanguage, noprivate, nomembers):
|
||||
def gen_args(first, last, fullname, displayname, initials, homedir, gecos,
|
||||
shell, email, principalexpiration, passwordexpiration, password,
|
||||
random, uid, gid, street, city, userstate, postalcode, phone,
|
||||
mobile, pager, fax, orgunit, title, carlicense, sshpubkey,
|
||||
userauthtype, userclass, radius, radiususer, departmentnumber,
|
||||
employeenumber, employeetype, preferredlanguage, smb_logon_script,
|
||||
smb_profile_path, smb_home_dir, smb_home_drive, idp, idp_user_id,
|
||||
noprivate, nomembers):
|
||||
# principal, manager, certificate and certmapdata are handled not in here
|
||||
_args = {}
|
||||
if first is not None:
|
||||
@@ -672,6 +771,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
_args["initials"] = initials
|
||||
if homedir is not None:
|
||||
_args["homedirectory"] = homedir
|
||||
if gecos is not None:
|
||||
_args["gecos"] = gecos
|
||||
if shell is not None:
|
||||
_args["loginshell"] = shell
|
||||
if email is not None and len(email) > 0:
|
||||
@@ -688,6 +789,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
_args["uidnumber"] = to_text(str(uid))
|
||||
if gid is not None:
|
||||
_args["gidnumber"] = to_text(str(gid))
|
||||
if street is not None:
|
||||
_args["street"] = street
|
||||
if city is not None:
|
||||
_args["l"] = city
|
||||
if userstate is not None:
|
||||
@@ -726,48 +829,57 @@ def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
_args["employeetype"] = employeetype
|
||||
if preferredlanguage is not None:
|
||||
_args["preferredlanguage"] = preferredlanguage
|
||||
if idp is not None:
|
||||
_args["ipaidpconfiglink"] = idp
|
||||
if idp_user_id is not None:
|
||||
_args["ipaidpsub"] = idp_user_id
|
||||
if noprivate is not None:
|
||||
_args["noprivate"] = noprivate
|
||||
if nomembers is not None:
|
||||
_args["no_members"] = nomembers
|
||||
if smb_logon_script is not None:
|
||||
_args["ipantlogonscript"] = smb_logon_script
|
||||
if smb_profile_path is not None:
|
||||
_args["ipantprofilepath"] = smb_profile_path
|
||||
if smb_home_dir is not None:
|
||||
_args["ipanthomedirectory"] = smb_home_dir
|
||||
if smb_home_drive is not None:
|
||||
_args["ipanthomedirectorydrive"] = smb_home_drive
|
||||
return _args
|
||||
|
||||
|
||||
def check_parameters( # pylint: disable=unused-argument
|
||||
module, state, action, first, last, fullname, displayname, initials,
|
||||
homedir, shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, city, phone, mobile,
|
||||
pager, fax, orgunit, title, manager, carlicense, sshpubkey,
|
||||
homedir, gecos, shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, street, city, phone,
|
||||
mobile, pager, fax, orgunit, title, manager, carlicense, sshpubkey,
|
||||
userauthtype, userclass, radius, radiususer, departmentnumber,
|
||||
employeenumber, employeetype, preferredlanguage, certificate,
|
||||
certmapdata, noprivate, nomembers, preserve, update_password):
|
||||
invalid = []
|
||||
if state == "present":
|
||||
if action == "member":
|
||||
invalid = ["first", "last", "fullname", "displayname", "initials",
|
||||
"homedir", "shell", "email", "principalexpiration",
|
||||
"passwordexpiration", "password", "random", "uid",
|
||||
"gid", "city", "phone", "mobile", "pager", "fax",
|
||||
"orgunit", "title", "carlicense", "sshpubkey",
|
||||
"userauthtype", "userclass", "radius", "radiususer",
|
||||
"departmentnumber", "employeenumber", "employeetype",
|
||||
"preferredlanguage", "noprivate", "nomembers",
|
||||
"preserve", "update_password"]
|
||||
|
||||
certmapdata, noprivate, nomembers, preserve, update_password,
|
||||
smb_logon_script, smb_profile_path, smb_home_dir, smb_home_drive,
|
||||
idp, ipa_user_id,
|
||||
):
|
||||
if state == "present" and action == "user":
|
||||
invalid = ["preserve"]
|
||||
else:
|
||||
invalid = ["first", "last", "fullname", "displayname", "initials",
|
||||
"homedir", "shell", "email", "principalexpiration",
|
||||
"passwordexpiration", "password", "random", "uid",
|
||||
"gid", "city", "phone", "mobile", "pager", "fax",
|
||||
"orgunit", "title", "carlicense", "sshpubkey",
|
||||
"userauthtype", "userclass", "radius", "radiususer",
|
||||
"departmentnumber", "employeenumber", "employeetype",
|
||||
"preferredlanguage", "noprivate", "nomembers",
|
||||
"update_password"]
|
||||
if action == "user":
|
||||
invalid.extend(["principal", "manager",
|
||||
"certificate", "certmapdata",
|
||||
])
|
||||
invalid = [
|
||||
"first", "last", "fullname", "displayname", "initials", "homedir",
|
||||
"shell", "email", "principalexpiration", "passwordexpiration",
|
||||
"password", "random", "uid", "gid", "street", "city", "phone",
|
||||
"mobile", "pager", "fax", "orgunit", "title", "carlicense",
|
||||
"sshpubkey", "userauthtype", "userclass", "radius", "radiususer",
|
||||
"departmentnumber", "employeenumber", "employeetype",
|
||||
"preferredlanguage", "noprivate", "nomembers", "update_password",
|
||||
"gecos", "smb_logon_script", "smb_profile_path", "smb_home_dir",
|
||||
"smb_home_drive", "idp", "idp_user_id"
|
||||
]
|
||||
|
||||
if state == "present" and action == "member":
|
||||
invalid.append("preserve")
|
||||
else:
|
||||
if action == "user":
|
||||
invalid.extend(
|
||||
["principal", "manager", "certificate", "certmapdata"])
|
||||
|
||||
if state != "absent" and preserve is not None:
|
||||
module.fail_json(
|
||||
@@ -801,6 +913,15 @@ def check_parameters( # pylint: disable=unused-argument
|
||||
module.fail_json(msg="certmapdata: subject is missing")
|
||||
|
||||
|
||||
def check_userauthtype(module, userauthtype):
|
||||
_invalid = module.ipa_command_invalid_param_choices(
|
||||
"user_add", "ipauserauthtype", userauthtype)
|
||||
if _invalid:
|
||||
module.fail_json(
|
||||
msg="The use of userauthtype '%s' is not supported "
|
||||
"by your IPA version" % "','".join(_invalid))
|
||||
|
||||
|
||||
def extend_emails(email, default_email_domain):
|
||||
if email is not None:
|
||||
return ["%s@%s" % (_email, default_email_domain)
|
||||
@@ -902,6 +1023,7 @@ def main():
|
||||
displayname=dict(type="str", default=None),
|
||||
initials=dict(type="str", default=None),
|
||||
homedir=dict(type="str", default=None),
|
||||
gecos=dict(type="str", default=None),
|
||||
shell=dict(type="str", aliases=["loginshell"], default=None),
|
||||
email=dict(type="list", elements="str", default=None),
|
||||
principal=dict(type="list", elements="str",
|
||||
@@ -917,6 +1039,7 @@ def main():
|
||||
random=dict(type='bool', default=None),
|
||||
uid=dict(type="int", aliases=["uidnumber"], default=None),
|
||||
gid=dict(type="int", aliases=["gidnumber"], default=None),
|
||||
street=dict(type="str", default=None),
|
||||
city=dict(type="str", default=None),
|
||||
userstate=dict(type="str", aliases=["st"], default=None),
|
||||
postalcode=dict(type="str", aliases=["zip"], default=None),
|
||||
@@ -934,7 +1057,8 @@ def main():
|
||||
default=None),
|
||||
userauthtype=dict(type='list', elements="str",
|
||||
aliases=["ipauserauthtype"], default=None,
|
||||
choices=['password', 'radius', 'otp', '']),
|
||||
choices=["password", "radius", "otp", "pkinit",
|
||||
"hardened", "idp", ""]),
|
||||
userclass=dict(type="list", elements="str", aliases=["class"],
|
||||
default=None),
|
||||
radius=dict(type="str", aliases=["ipatokenradiusconfiglink"],
|
||||
@@ -945,6 +1069,17 @@ def main():
|
||||
departmentnumber=dict(type="list", elements="str", default=None),
|
||||
employeenumber=dict(type="str", default=None),
|
||||
employeetype=dict(type="str", default=None),
|
||||
smb_logon_script=dict(type="str", default=None,
|
||||
aliases=["ipantlogonscript"]),
|
||||
smb_profile_path=dict(type="str", default=None,
|
||||
aliases=["ipantprofilepath"]),
|
||||
smb_home_dir=dict(type="str", default=None,
|
||||
aliases=["ipanthomedirectory"]),
|
||||
smb_home_drive=dict(type="str", default=None,
|
||||
choices=[
|
||||
("%c:" % chr(x))
|
||||
for x in range(ord('A'), ord('Z') + 1)
|
||||
] + [""], aliases=["ipanthomedirectorydrive"]),
|
||||
preferredlanguage=dict(type="str", default=None),
|
||||
certificate=dict(type="list", elements="str",
|
||||
aliases=["usercertificate"], default=None),
|
||||
@@ -959,6 +1094,9 @@ def main():
|
||||
elements='dict', required=False),
|
||||
noprivate=dict(type='bool', default=None),
|
||||
nomembers=dict(type='bool', default=None),
|
||||
idp=dict(type="str", default=None, aliases=['ipaidpconfiglink']),
|
||||
idp_user_id=dict(type="str", default=None,
|
||||
aliases=['ipaidpconfiglink']),
|
||||
)
|
||||
|
||||
ansible_module = IPAAnsibleModule(
|
||||
@@ -1015,6 +1153,7 @@ def main():
|
||||
displayname = ansible_module.params_get("displayname")
|
||||
initials = ansible_module.params_get("initials")
|
||||
homedir = ansible_module.params_get("homedir")
|
||||
gecos = ansible_module.params_get("gecos")
|
||||
shell = ansible_module.params_get("shell")
|
||||
email = ansible_module.params_get("email")
|
||||
principal = ansible_module.params_get("principal")
|
||||
@@ -1033,6 +1172,7 @@ def main():
|
||||
random = ansible_module.params_get("random")
|
||||
uid = ansible_module.params_get("uid")
|
||||
gid = ansible_module.params_get("gid")
|
||||
street = ansible_module.params_get("street")
|
||||
city = ansible_module.params_get("city")
|
||||
userstate = ansible_module.params_get("userstate")
|
||||
postalcode = ansible_module.params_get("postalcode")
|
||||
@@ -1055,6 +1195,12 @@ def main():
|
||||
employeenumber = ansible_module.params_get("employeenumber")
|
||||
employeetype = ansible_module.params_get("employeetype")
|
||||
preferredlanguage = ansible_module.params_get("preferredlanguage")
|
||||
smb_logon_script = ansible_module.params_get("smb_logon_script")
|
||||
smb_profile_path = ansible_module.params_get("smb_profile_path")
|
||||
smb_home_dir = ansible_module.params_get("smb_home_dir")
|
||||
smb_home_drive = ansible_module.params_get("smb_home_drive")
|
||||
idp = ansible_module.params_get("idp")
|
||||
idp_user_id = ansible_module.params_get("idp_user_id")
|
||||
certificate = ansible_module.params_get("certificate")
|
||||
certmapdata = ansible_module.params_get("certmapdata")
|
||||
noprivate = ansible_module.params_get("noprivate")
|
||||
@@ -1080,13 +1226,15 @@ def main():
|
||||
|
||||
check_parameters(
|
||||
ansible_module, state, action,
|
||||
first, last, fullname, displayname, initials, homedir, shell, email,
|
||||
first, last, fullname, displayname, initials, homedir, gecos, shell,
|
||||
email,
|
||||
principal, principalexpiration, passwordexpiration, password, random,
|
||||
uid, gid, city, phone, mobile, pager, fax, orgunit, title, manager,
|
||||
carlicense, sshpubkey, userauthtype, userclass, radius, radiususer,
|
||||
departmentnumber, employeenumber, employeetype, preferredlanguage,
|
||||
certificate, certmapdata, noprivate, nomembers, preserve,
|
||||
update_password)
|
||||
uid, gid, street, city, phone, mobile, pager, fax, orgunit, title,
|
||||
manager, carlicense, sshpubkey, userauthtype, userclass, radius,
|
||||
radiususer, departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, certificate, certmapdata, noprivate, nomembers,
|
||||
preserve, update_password, smb_logon_script, smb_profile_path,
|
||||
smb_home_dir, smb_home_drive, idp, idp_user_id)
|
||||
certmapdata = convert_certmapdata(certmapdata)
|
||||
|
||||
# Use users if names is None
|
||||
@@ -1105,6 +1253,10 @@ def main():
|
||||
|
||||
server_realm = ansible_module.ipa_get_realm()
|
||||
|
||||
# Check API specific parameters
|
||||
|
||||
check_userauthtype(ansible_module, userauthtype)
|
||||
|
||||
# Default email domain
|
||||
|
||||
result = ansible_module.ipa_command_no_name("config_show", {})
|
||||
@@ -1133,6 +1285,7 @@ def main():
|
||||
displayname = user.get("displayname")
|
||||
initials = user.get("initials")
|
||||
homedir = user.get("homedir")
|
||||
gecos = user.get("gecos")
|
||||
shell = user.get("shell")
|
||||
email = user.get("email")
|
||||
principal = user.get("principal")
|
||||
@@ -1150,6 +1303,7 @@ def main():
|
||||
random = user.get("random")
|
||||
uid = user.get("uid")
|
||||
gid = user.get("gid")
|
||||
street = user.get("street")
|
||||
city = user.get("city")
|
||||
userstate = user.get("userstate")
|
||||
postalcode = user.get("postalcode")
|
||||
@@ -1170,6 +1324,12 @@ def main():
|
||||
employeenumber = user.get("employeenumber")
|
||||
employeetype = user.get("employeetype")
|
||||
preferredlanguage = user.get("preferredlanguage")
|
||||
smb_logon_script = user.get("smb_logon_script")
|
||||
smb_profile_path = user.get("smb_profile_path")
|
||||
smb_home_dir = user.get("smb_home_dir")
|
||||
smb_home_drive = user.get("smb_home_drive")
|
||||
idp = user.get("idp")
|
||||
idp_user_id = user.get("idp_user_id")
|
||||
certificate = user.get("certificate")
|
||||
certmapdata = user.get("certmapdata")
|
||||
noprivate = user.get("noprivate")
|
||||
@@ -1178,16 +1338,21 @@ def main():
|
||||
check_parameters(
|
||||
ansible_module, state, action,
|
||||
first, last, fullname, displayname, initials, homedir,
|
||||
shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, city,
|
||||
phone, mobile, pager, fax, orgunit, title, manager,
|
||||
gecos, shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, street,
|
||||
city, phone, mobile, pager, fax, orgunit, title, manager,
|
||||
carlicense, sshpubkey, userauthtype, userclass, radius,
|
||||
radiususer, departmentnumber, employeenumber,
|
||||
employeetype, preferredlanguage, certificate,
|
||||
certmapdata, noprivate, nomembers, preserve,
|
||||
update_password)
|
||||
update_password, smb_logon_script, smb_profile_path,
|
||||
smb_home_dir, smb_home_drive, idp, idp_user_id)
|
||||
certmapdata = convert_certmapdata(certmapdata)
|
||||
|
||||
# Check API specific parameters
|
||||
|
||||
check_userauthtype(ansible_module, userauthtype)
|
||||
|
||||
# Extend email addresses
|
||||
|
||||
email = extend_emails(email, default_email_domain)
|
||||
@@ -1227,6 +1392,34 @@ def main():
|
||||
msg="The use of certmapdata is not supported by "
|
||||
"your IPA version")
|
||||
|
||||
# Check if SMB attributes are available
|
||||
if (
|
||||
any([
|
||||
smb_logon_script, smb_profile_path, smb_home_dir,
|
||||
smb_home_drive
|
||||
])
|
||||
and not ansible_module.ipa_command_param_exists(
|
||||
"user_mod", "ipanthomedirectory"
|
||||
)
|
||||
):
|
||||
ansible_module.fail_json(
|
||||
msg="The use of smb_logon_script, smb_profile_path, "
|
||||
"smb_profile_path, and smb_home_drive is not supported "
|
||||
"by your IPA version")
|
||||
|
||||
# Check if IdP support is available
|
||||
require_idp = (
|
||||
idp is not None
|
||||
or idp_user_id is not None
|
||||
or userauthtype == "idp"
|
||||
)
|
||||
has_idp_support = ansible_module.ipa_command_param_exists(
|
||||
"user_add", "ipaidpconfiglink"
|
||||
)
|
||||
if require_idp and not has_idp_support:
|
||||
ansible_module.fail_json(
|
||||
msg="Your IPA version does not support External IdP.")
|
||||
|
||||
# Make sure user exists
|
||||
res_find = find_user(ansible_module, name)
|
||||
|
||||
@@ -1235,12 +1428,16 @@ def main():
|
||||
# Generate args
|
||||
args = gen_args(
|
||||
first, last, fullname, displayname, initials, homedir,
|
||||
gecos,
|
||||
shell, email, principalexpiration, passwordexpiration,
|
||||
password, random, uid, gid, city, userstate, postalcode,
|
||||
phone, mobile, pager, fax, orgunit, title, carlicense,
|
||||
sshpubkey, userauthtype, userclass, radius, radiususer,
|
||||
departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, noprivate, nomembers)
|
||||
password, random, uid, gid, street, city, userstate,
|
||||
postalcode, phone, mobile, pager, fax, orgunit, title,
|
||||
carlicense, sshpubkey, userauthtype, userclass, radius,
|
||||
radiususer, departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, smb_logon_script, smb_profile_path,
|
||||
smb_home_dir, smb_home_drive, idp, idp_user_id, noprivate,
|
||||
nomembers,
|
||||
)
|
||||
|
||||
if action == "user":
|
||||
# Found the user
|
||||
@@ -1276,8 +1473,21 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Last name is needed")
|
||||
|
||||
smb_attrs = {
|
||||
k: args[k]
|
||||
for k in [
|
||||
"ipanthomedirectory",
|
||||
"ipanthomedirectorydrive",
|
||||
"ipantlogonscript",
|
||||
"ipantprofilepath",
|
||||
]
|
||||
if k in args
|
||||
}
|
||||
for key in smb_attrs.keys():
|
||||
del args[key]
|
||||
commands.append([name, "user_add", args])
|
||||
|
||||
if smb_attrs:
|
||||
commands.append([name, "user_mod", smb_attrs])
|
||||
# Handle members: principal, manager, certificate and
|
||||
# certmapdata
|
||||
if res_find is not None:
|
||||
|
||||
@@ -977,12 +977,14 @@ def main():
|
||||
changed = 'Archived data into' in result['summary']
|
||||
elif command == 'vault_retrieve':
|
||||
if 'result' not in result:
|
||||
# pylint: disable=W0012,broad-exception-raised
|
||||
raise Exception("No result obtained.")
|
||||
if "data" in result["result"]:
|
||||
data_return = exit_args.setdefault("vault", {})
|
||||
data_return["data"] = result["result"]["data"]
|
||||
else:
|
||||
if not datafile_out:
|
||||
# pylint: disable=W0012,broad-exception-raised
|
||||
raise Exception("No data retrieved.")
|
||||
changed = False
|
||||
else:
|
||||
@@ -993,7 +995,7 @@ def main():
|
||||
changed = True
|
||||
except ipalib_errors.EmptyModlist:
|
||||
result = {}
|
||||
except Exception as exception:
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
ansible_module.fail_json(
|
||||
msg="%s: %s: %s" % (command, name, str(exception)))
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
-r requirements-tests.txt
|
||||
ipdb==0.13.4
|
||||
pre-commit==2.20.0
|
||||
flake8==5.0.3
|
||||
flake8-bugbear==22.10.27
|
||||
pylint==2.14.4
|
||||
wrapt == 1.14.0
|
||||
pydocstyle==6.0.0
|
||||
yamllint==1.28.0
|
||||
ansible-lint==6.6.1
|
||||
flake8==6.0.0
|
||||
flake8-bugbear
|
||||
pylint==2.17.2
|
||||
wrapt==1.14.1
|
||||
pydocstyle==6.3.0
|
||||
yamllint==1.32.0
|
||||
ansible-lint
|
||||
|
||||
@@ -33,15 +33,16 @@ Supported Distributions
|
||||
-----------------------
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* CentOS Stream 8+
|
||||
* Fedora 26+
|
||||
* Ubuntu
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -6,7 +6,7 @@ galaxy_info:
|
||||
description: A role to backup and restore an IPA server
|
||||
company: Red Hat, Inc
|
||||
license: GPLv3
|
||||
min_ansible_version: "2.8"
|
||||
min_ansible_version: "2.13"
|
||||
platforms:
|
||||
- name: Fedora
|
||||
versions:
|
||||
|
||||
@@ -24,15 +24,17 @@ Supported Distributions
|
||||
-----------------------
|
||||
|
||||
* RHEL/CentOS 7.4+
|
||||
* CentOS Stream 8+
|
||||
* Fedora 26+
|
||||
* Ubuntu
|
||||
* Debian
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -132,7 +132,7 @@ def main():
|
||||
else:
|
||||
get_ca_certs(fstore, options, servers[0], basedn, realm)
|
||||
changed = True
|
||||
del os.environ['KRB5_CONFIG']
|
||||
os.environ.pop('KRB5_CONFIG', None)
|
||||
except errors.FileError as e:
|
||||
module.fail_json(msg='%s' % e)
|
||||
except Exception as e:
|
||||
|
||||
@@ -123,7 +123,7 @@ def temp_kdestroy(ccache_dir, ccache_name):
|
||||
"""Destroy temporary ticket and remove temporary ccache."""
|
||||
if ccache_name is not None:
|
||||
run([paths.KDESTROY, '-c', ccache_name], raiseonerr=False)
|
||||
del os.environ['KRB5CCNAME']
|
||||
os.environ.pop('KRB5CCNAME', None)
|
||||
if ccache_dir is not None:
|
||||
shutil.rmtree(ccache_dir, ignore_errors=True)
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ def main():
|
||||
get_ca_cert(fstore, options, servers[0], basedn)
|
||||
else:
|
||||
get_ca_certs(fstore, options, servers[0], basedn, realm)
|
||||
del os.environ['KRB5_CONFIG']
|
||||
os.environ.pop('KRB5_CONFIG', None)
|
||||
except errors.FileError as e:
|
||||
module.fail_json(msg='%s' % e)
|
||||
except Exception as e:
|
||||
|
||||
@@ -6,7 +6,7 @@ galaxy_info:
|
||||
description: A role to join a machine to an IPA domain
|
||||
company: Red Hat, Inc
|
||||
license: GPLv3
|
||||
min_ansible_version: "2.8"
|
||||
min_ansible_version: "2.13"
|
||||
platforms:
|
||||
- name: Fedora
|
||||
versions:
|
||||
|
||||
@@ -307,8 +307,7 @@ try:
|
||||
|
||||
else:
|
||||
# IPA version < 4.4
|
||||
|
||||
raise Exception("freeipa version '%s' is too old" % VERSION)
|
||||
raise RuntimeError("freeipa version '%s' is too old" % VERSION)
|
||||
|
||||
except ImportError as _err:
|
||||
ANSIBLE_IPA_CLIENT_MODULE_IMPORT_ERROR = str(_err)
|
||||
|
||||
@@ -27,15 +27,16 @@ Supported Distributions
|
||||
-----------------------
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* CentOS Stream 8+
|
||||
* Fedora 26+
|
||||
* Ubuntu
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* Ansible version: 2.13+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user