Skip to content
Snippets Groups Projects
Unverified Commit 88d4a36e authored by Thomas Woerner's avatar Thomas Woerner Committed by GitHub
Browse files

Merge pull request #1055 from rjeffman/ipauser_idp_attrs

ipauser: Support for External IdP attributes.
parents c9e86564 6fa82236
No related branches found
No related tags found
No related merge requests found
...@@ -445,6 +445,8 @@ Variable | Description | Required ...@@ -445,6 +445,8 @@ Variable | Description | Required
`employeenumber` | Employee Number | no `employeenumber` | Employee Number | no
`employeetype` | Employee Type | no `employeetype` | Employee Type | no
`preferredlanguage` | Preferred Language | 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 `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 `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
&nbsp; | `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no &nbsp; | `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no
......
---
- 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
...@@ -271,6 +271,16 @@ options: ...@@ -271,6 +271,16 @@ options:
description: Preferred Language description: Preferred Language
type: str type: str
required: false 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: certificate:
description: List of base-64 encoded user certificates description: List of base-64 encoded user certificates
type: list type: list
...@@ -528,6 +538,16 @@ options: ...@@ -528,6 +538,16 @@ options:
description: Preferred Language description: Preferred Language
type: str type: str
required: false 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: certificate:
description: List of base-64 encoded user certificates description: List of base-64 encoded user certificates
type: list type: list
...@@ -735,8 +755,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos, ...@@ -735,8 +755,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos,
mobile, pager, fax, orgunit, title, carlicense, sshpubkey, mobile, pager, fax, orgunit, title, carlicense, sshpubkey,
userauthtype, userclass, radius, radiususer, departmentnumber, userauthtype, userclass, radius, radiususer, departmentnumber,
employeenumber, employeetype, preferredlanguage, smb_logon_script, employeenumber, employeetype, preferredlanguage, smb_logon_script,
smb_profile_path, smb_home_dir, smb_home_drive, noprivate, smb_profile_path, smb_home_dir, smb_home_drive, idp, idp_user_id,
nomembers): noprivate, nomembers):
# principal, manager, certificate and certmapdata are handled not in here # principal, manager, certificate and certmapdata are handled not in here
_args = {} _args = {}
if first is not None: if first is not None:
...@@ -809,6 +829,10 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos, ...@@ -809,6 +829,10 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos,
_args["employeetype"] = employeetype _args["employeetype"] = employeetype
if preferredlanguage is not None: if preferredlanguage is not None:
_args["preferredlanguage"] = preferredlanguage _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: if noprivate is not None:
_args["noprivate"] = noprivate _args["noprivate"] = noprivate
if nomembers is not None: if nomembers is not None:
...@@ -833,6 +857,7 @@ def check_parameters( # pylint: disable=unused-argument ...@@ -833,6 +857,7 @@ def check_parameters( # pylint: disable=unused-argument
employeenumber, employeetype, preferredlanguage, certificate, employeenumber, employeetype, preferredlanguage, certificate,
certmapdata, noprivate, nomembers, preserve, update_password, certmapdata, noprivate, nomembers, preserve, update_password,
smb_logon_script, smb_profile_path, smb_home_dir, smb_home_drive, smb_logon_script, smb_profile_path, smb_home_dir, smb_home_drive,
idp, ipa_user_id,
): ):
if state == "present" and action == "user": if state == "present" and action == "user":
invalid = ["preserve"] invalid = ["preserve"]
...@@ -846,7 +871,7 @@ def check_parameters( # pylint: disable=unused-argument ...@@ -846,7 +871,7 @@ def check_parameters( # pylint: disable=unused-argument
"departmentnumber", "employeenumber", "employeetype", "departmentnumber", "employeenumber", "employeetype",
"preferredlanguage", "noprivate", "nomembers", "update_password", "preferredlanguage", "noprivate", "nomembers", "update_password",
"gecos", "smb_logon_script", "smb_profile_path", "smb_home_dir", "gecos", "smb_logon_script", "smb_profile_path", "smb_home_dir",
"smb_home_drive", "smb_home_drive", "idp", "idp_user_id"
] ]
if state == "present" and action == "member": if state == "present" and action == "member":
...@@ -1069,6 +1094,9 @@ def main(): ...@@ -1069,6 +1094,9 @@ def main():
elements='dict', required=False), elements='dict', required=False),
noprivate=dict(type='bool', default=None), noprivate=dict(type='bool', default=None),
nomembers=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( ansible_module = IPAAnsibleModule(
...@@ -1171,6 +1199,8 @@ def main(): ...@@ -1171,6 +1199,8 @@ def main():
smb_profile_path = ansible_module.params_get("smb_profile_path") smb_profile_path = ansible_module.params_get("smb_profile_path")
smb_home_dir = ansible_module.params_get("smb_home_dir") smb_home_dir = ansible_module.params_get("smb_home_dir")
smb_home_drive = ansible_module.params_get("smb_home_drive") 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") certificate = ansible_module.params_get("certificate")
certmapdata = ansible_module.params_get("certmapdata") certmapdata = ansible_module.params_get("certmapdata")
noprivate = ansible_module.params_get("noprivate") noprivate = ansible_module.params_get("noprivate")
...@@ -1204,7 +1234,7 @@ def main(): ...@@ -1204,7 +1234,7 @@ def main():
radiususer, departmentnumber, employeenumber, employeetype, radiususer, departmentnumber, employeenumber, employeetype,
preferredlanguage, certificate, certmapdata, noprivate, nomembers, preferredlanguage, certificate, certmapdata, noprivate, nomembers,
preserve, update_password, smb_logon_script, smb_profile_path, preserve, update_password, smb_logon_script, smb_profile_path,
smb_home_dir, smb_home_drive) smb_home_dir, smb_home_drive, idp, idp_user_id)
certmapdata = convert_certmapdata(certmapdata) certmapdata = convert_certmapdata(certmapdata)
# Use users if names is None # Use users if names is None
...@@ -1298,6 +1328,8 @@ def main(): ...@@ -1298,6 +1328,8 @@ def main():
smb_profile_path = user.get("smb_profile_path") smb_profile_path = user.get("smb_profile_path")
smb_home_dir = user.get("smb_home_dir") smb_home_dir = user.get("smb_home_dir")
smb_home_drive = user.get("smb_home_drive") smb_home_drive = user.get("smb_home_drive")
idp = user.get("idp")
idp_user_id = user.get("idp_user_id")
certificate = user.get("certificate") certificate = user.get("certificate")
certmapdata = user.get("certmapdata") certmapdata = user.get("certmapdata")
noprivate = user.get("noprivate") noprivate = user.get("noprivate")
...@@ -1314,7 +1346,7 @@ def main(): ...@@ -1314,7 +1346,7 @@ def main():
employeetype, preferredlanguage, certificate, employeetype, preferredlanguage, certificate,
certmapdata, noprivate, nomembers, preserve, certmapdata, noprivate, nomembers, preserve,
update_password, smb_logon_script, smb_profile_path, update_password, smb_logon_script, smb_profile_path,
smb_home_dir, smb_home_drive) smb_home_dir, smb_home_drive, idp, idp_user_id)
certmapdata = convert_certmapdata(certmapdata) certmapdata = convert_certmapdata(certmapdata)
# Check API specific parameters # Check API specific parameters
...@@ -1375,6 +1407,19 @@ def main(): ...@@ -1375,6 +1407,19 @@ def main():
"smb_profile_path, and smb_home_drive is not supported " "smb_profile_path, and smb_home_drive is not supported "
"by your IPA version") "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 # Make sure user exists
res_find = find_user(ansible_module, name) res_find = find_user(ansible_module, name)
...@@ -1390,7 +1435,9 @@ def main(): ...@@ -1390,7 +1435,9 @@ def main():
carlicense, sshpubkey, userauthtype, userclass, radius, carlicense, sshpubkey, userauthtype, userclass, radius,
radiususer, departmentnumber, employeenumber, employeetype, radiususer, departmentnumber, employeenumber, employeetype,
preferredlanguage, smb_logon_script, smb_profile_path, preferredlanguage, smb_logon_script, smb_profile_path,
smb_home_dir, smb_home_drive, noprivate, nomembers) smb_home_dir, smb_home_drive, idp, idp_user_id, noprivate,
nomembers,
)
if action == "user": if action == "user":
# Found the user # Found the user
......
---
- name: Test user
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: false
gather_facts: false
module_defaults:
ipauser:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
tasks:
- name: Include tasks ../env_freeipa_facts.yml
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
# CLEANUP TEST ITEMS
- name: Ensure user idpuser is absent
ipauser:
name: idpuser
state: absent
# CREATE TEST ITEMS
- name: Run tests if FreeIPA 4.10.0+ is installed
when: ipa_version is version('4.10.0', '>=')
block:
- name: Ensure IDP provider is present
# TODO: Use an ansible-freeipa plugin instead of 'shell'
ansible.builtin.shell:
cmd: |
kinit -c test_krb5_cache admin <<< SomeADMINpassword
KRB5CCNAME=test_krb5_cache ipa idp-add keycloak --provider keycloak \
--org master \
--base-url https://client.ipademo.local:8443/auth \
--client-id ipa_oidc_client \
--secret <<< $(echo -e "Secret123\nSecret123")
kdestroy -c test_krb5_cache -q -A
register: addidp
failed_when:
- '"Added Identity Provider" not in addidp.stdout'
- '"already exists" not in addidp.stderr'
# TESTS
- name: Ensure user idpuser is present
ipauser:
name: idpuser
first: IDP
last: User
userauthtype: idp
idp: keycloak
idp_user_id: "idpuser@ipademo.local"
register: result
failed_when: not result.changed or result.failed
- name: Ensure user idpuser is present again
ipauser:
name: idpuser
first: IDP
last: User
userauthtype: idp
idp: keycloak
idp_user_id: "idpuser@ipademo.local"
register: result
failed_when: result.changed or result.failed
- name: Clear 'idp_user_id'
ipauser:
name: idpuser
idp_user_id: ""
register: result
failed_when: not result.changed or result.failed
- name: Clear 'idp'
ipauser:
name: idpuser
idp: ""
register: result
failed_when: not result.changed or result.failed
- name: Ensure user idpuser is absent
ipauser:
name: idpuser
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Ensure user idpuser is absent again
ipauser:
name: idpuser
state: absent
register: result
failed_when: result.changed or result.failed
# CLEANUP TEST ITEMS
- name: Ensure IDP provider is absent
# TODO: Use an ansible-freeipa plugin instead of 'shell'
ansible.builtin.shell:
cmd: |
kinit -c test_krb5_cache admin <<< SomeADMINpassword
ipa idp-del keycloak
kdestroy -c test_krb5_cache -q -A
always:
- name: Ensure user idpuser is absent
ipauser:
name: idpuser
state: absent
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment