mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-07-31 20:04:45 +00:00
Merge pull request #262 from t-woerner/ipauser_fix_certmapdata
ipauser: Fix certmapdata, add missing certmapdata data option
This commit is contained in:
@@ -417,10 +417,11 @@ Variable | Description | Required
|
|||||||
`employeetype` | Employee Type | no
|
`employeetype` | Employee Type | no
|
||||||
`preferredlanguage` | Preferred Language | no
|
`preferredlanguage` | Preferred Language | 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 `certificate` or `issuer` together with `subject` need to be specified. <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
|
||||||
| `certificate` - Base-64 encoded user certificate | no
|
| `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no
|
||||||
| `issuer` - Issuer of the certificate | no
|
| `issuer` - Issuer of the certificate, only usable together with `usbject` option. | no
|
||||||
| `subject` - Subject of the certificate | no
|
| `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
|
`noprivate` | Do not create user private group. (bool) | no
|
||||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from ipapython.ipautil import kinit_password, kinit_keytab
|
from ipapython.ipautil import kinit_password, kinit_keytab
|
||||||
from ipapython.ipautil import run
|
from ipapython.ipautil import run
|
||||||
|
from ipapython.dn import DN
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipalib.krb_utils import get_credentials_if_valid
|
from ipalib.krb_utils import get_credentials_if_valid
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
@@ -48,6 +49,13 @@ try:
|
|||||||
from ipalib.x509 import Encoding
|
from ipalib.x509 import Encoding
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from cryptography.hazmat.primitives.serialization import Encoding
|
from cryptography.hazmat.primitives.serialization import Encoding
|
||||||
|
|
||||||
|
try:
|
||||||
|
from ipalib.x509 import load_pem_x509_certificate
|
||||||
|
except ImportError:
|
||||||
|
from ipalib.x509 import load_certificate
|
||||||
|
load_pem_x509_certificate = None
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
import base64
|
import base64
|
||||||
import six
|
import six
|
||||||
@@ -167,6 +175,11 @@ def api_command_no_name(module, command, args):
|
|||||||
return api.Command[command](**args)
|
return api.Command[command](**args)
|
||||||
|
|
||||||
|
|
||||||
|
def api_check_command(command):
|
||||||
|
"""Return if command exists in command list."""
|
||||||
|
return command in api.Command
|
||||||
|
|
||||||
|
|
||||||
def api_check_param(command, name):
|
def api_check_param(command, name):
|
||||||
"""Check if param exists in command param list."""
|
"""Check if param exists in command param list."""
|
||||||
return name in api.Command[command].params
|
return name in api.Command[command].params
|
||||||
@@ -323,6 +336,30 @@ def encode_certificate(cert):
|
|||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
|
|
||||||
|
def load_cert_from_str(cert):
|
||||||
|
cert = cert.strip()
|
||||||
|
if not cert.startswith("-----BEGIN CERTIFICATE-----"):
|
||||||
|
cert = "-----BEGIN CERTIFICATE-----\n" + cert
|
||||||
|
if not cert.endswith("-----END CERTIFICATE-----"):
|
||||||
|
cert += "\n-----END CERTIFICATE-----"
|
||||||
|
|
||||||
|
if load_pem_x509_certificate is not None:
|
||||||
|
cert = load_pem_x509_certificate(cert.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
cert = load_certificate(cert.encode('utf-8'))
|
||||||
|
return cert
|
||||||
|
|
||||||
|
|
||||||
|
def DN_x500_text(text):
|
||||||
|
if hasattr(DN, "x500_text"):
|
||||||
|
return DN(text).x500_text()
|
||||||
|
else:
|
||||||
|
# Emulate x500_text
|
||||||
|
dn = DN(text)
|
||||||
|
dn.rdns = reversed(dn.rdns)
|
||||||
|
return str(dn)
|
||||||
|
|
||||||
|
|
||||||
def is_valid_port(port):
|
def is_valid_port(port):
|
||||||
if not isinstance(port, int):
|
if not isinstance(port, int):
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -186,7 +186,9 @@ options:
|
|||||||
description: List of base-64 encoded user certificates
|
description: List of base-64 encoded user certificates
|
||||||
required: false
|
required: false
|
||||||
certmapdata:
|
certmapdata:
|
||||||
description: List of certificate mappings
|
description:
|
||||||
|
- List of certificate mappings
|
||||||
|
- Only usable with IPA versions 4.5 and up.
|
||||||
options:
|
options:
|
||||||
certificate:
|
certificate:
|
||||||
description: Base-64 encoded user certificate
|
description: Base-64 encoded user certificate
|
||||||
@@ -197,6 +199,9 @@ options:
|
|||||||
subject:
|
subject:
|
||||||
description: Subject of the certificate
|
description: Subject of the certificate
|
||||||
required: false
|
required: false
|
||||||
|
data:
|
||||||
|
description: Certmap data
|
||||||
|
required: false
|
||||||
required: false
|
required: false
|
||||||
noprivate:
|
noprivate:
|
||||||
description: Don't create user private group
|
description: Don't create user private group
|
||||||
@@ -346,7 +351,9 @@ options:
|
|||||||
description: List of base-64 encoded user certificates
|
description: List of base-64 encoded user certificates
|
||||||
required: false
|
required: false
|
||||||
certmapdata:
|
certmapdata:
|
||||||
description: List of certificate mappings
|
description:
|
||||||
|
- List of certificate mappings
|
||||||
|
- Only usable with IPA versions 4.5 and up.
|
||||||
options:
|
options:
|
||||||
certificate:
|
certificate:
|
||||||
description: Base-64 encoded user certificate
|
description: Base-64 encoded user certificate
|
||||||
@@ -357,6 +364,9 @@ options:
|
|||||||
subject:
|
subject:
|
||||||
description: Subject of the certificate
|
description: Subject of the certificate
|
||||||
required: false
|
required: false
|
||||||
|
data:
|
||||||
|
description: Certmap data
|
||||||
|
required: false
|
||||||
required: false
|
required: false
|
||||||
noprivate:
|
noprivate:
|
||||||
description: Don't create user private group
|
description: Don't create user private group
|
||||||
@@ -467,7 +477,8 @@ from ansible.module_utils._text import to_text
|
|||||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||||
temp_kdestroy, valid_creds, api_connect, api_command, date_format, \
|
temp_kdestroy, valid_creds, api_connect, api_command, date_format, \
|
||||||
compare_args_ipa, module_params_get, api_check_param, api_get_realm, \
|
compare_args_ipa, module_params_get, api_check_param, api_get_realm, \
|
||||||
api_command_no_name, gen_add_del_lists, encode_certificate
|
api_command_no_name, gen_add_del_lists, encode_certificate, \
|
||||||
|
load_cert_from_str, DN_x500_text, api_check_command
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
@@ -645,13 +656,21 @@ def check_parameters(module, state, action,
|
|||||||
certificate = x.get("certificate")
|
certificate = x.get("certificate")
|
||||||
issuer = x.get("issuer")
|
issuer = x.get("issuer")
|
||||||
subject = x.get("subject")
|
subject = x.get("subject")
|
||||||
|
data = x.get("data")
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
if certificate is not None or issuer is not None or \
|
||||||
|
subject is not None:
|
||||||
|
module.fail_json(
|
||||||
|
msg="certmapdata: data can not be used with "
|
||||||
|
"certificate, issuer or subject")
|
||||||
|
check_certmapdata(data)
|
||||||
if certificate is not None \
|
if certificate is not None \
|
||||||
and (issuer is not None or subject is not None):
|
and (issuer is not None or subject is not None):
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg="certmapdata: certificate can not be used with "
|
msg="certmapdata: certificate can not be used with "
|
||||||
"issuer or subject")
|
"issuer or subject")
|
||||||
if certificate is None:
|
if data is None and certificate is None:
|
||||||
if issuer is None:
|
if issuer is None:
|
||||||
module.fail_json(msg="certmapdata: issuer is missing")
|
module.fail_json(msg="certmapdata: issuer is missing")
|
||||||
if subject is None:
|
if subject is None:
|
||||||
@@ -666,19 +685,48 @@ def extend_emails(email, default_email_domain):
|
|||||||
return email
|
return email
|
||||||
|
|
||||||
|
|
||||||
def gen_certmapdata_args(certmapdata):
|
def convert_certmapdata(certmapdata):
|
||||||
certificate = certmapdata.get("certificate")
|
if certmapdata is None:
|
||||||
issuer = certmapdata.get("issuer")
|
return None
|
||||||
subject = certmapdata.get("subject")
|
|
||||||
|
|
||||||
_args = {}
|
_result = []
|
||||||
if certificate is not None:
|
for x in certmapdata:
|
||||||
_args["certificate"] = certificate
|
certificate = x.get("certificate")
|
||||||
if issuer is not None:
|
issuer = x.get("issuer")
|
||||||
_args["issuer"] = issuer
|
subject = x.get("subject")
|
||||||
if subject is not None:
|
data = x.get("data")
|
||||||
_args["subject"] = subject
|
|
||||||
return _args
|
if data is None:
|
||||||
|
if issuer is None and subject is None:
|
||||||
|
cert = load_cert_from_str(certificate)
|
||||||
|
issuer = cert.issuer
|
||||||
|
subject = cert.subject
|
||||||
|
|
||||||
|
_result.append("X509:<I>%s<S>%s" % (DN_x500_text(issuer),
|
||||||
|
DN_x500_text(subject)))
|
||||||
|
else:
|
||||||
|
_result.append(data)
|
||||||
|
|
||||||
|
return _result
|
||||||
|
|
||||||
|
|
||||||
|
def check_certmapdata(data):
|
||||||
|
if not data.startswith("X509:"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
i = data.find("<I>", 4)
|
||||||
|
s = data.find("<S>", i)
|
||||||
|
issuer = data[i+3:s]
|
||||||
|
subject = data[s+3:]
|
||||||
|
|
||||||
|
if i < 0 or s < 0 or "CN" not in issuer or "CN" not in subject:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def gen_certmapdata_args(certmapdata):
|
||||||
|
return {"ipacertmapdata": to_text(certmapdata)}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -740,7 +788,8 @@ def main():
|
|||||||
# Here certificate is a simple string
|
# Here certificate is a simple string
|
||||||
certificate=dict(type="str", default=None),
|
certificate=dict(type="str", default=None),
|
||||||
issuer=dict(type="str", default=None),
|
issuer=dict(type="str", default=None),
|
||||||
subject=dict(type="str", default=None)
|
subject=dict(type="str", default=None),
|
||||||
|
data=dict(type="str", default=None)
|
||||||
),
|
),
|
||||||
elements='dict', required=False),
|
elements='dict', required=False),
|
||||||
noprivate=dict(type='bool', default=None),
|
noprivate=dict(type='bool', default=None),
|
||||||
@@ -875,6 +924,7 @@ def main():
|
|||||||
departmentnumber, employeenumber, employeetype, preferredlanguage,
|
departmentnumber, employeenumber, employeetype, preferredlanguage,
|
||||||
certificate, certmapdata, noprivate, nomembers, preserve,
|
certificate, certmapdata, noprivate, nomembers, preserve,
|
||||||
update_password)
|
update_password)
|
||||||
|
certmapdata = convert_certmapdata(certmapdata)
|
||||||
|
|
||||||
# Use users if names is None
|
# Use users if names is None
|
||||||
if users is not None:
|
if users is not None:
|
||||||
@@ -972,6 +1022,7 @@ def main():
|
|||||||
employeetype, preferredlanguage, certificate,
|
employeetype, preferredlanguage, certificate,
|
||||||
certmapdata, noprivate, nomembers, preserve,
|
certmapdata, noprivate, nomembers, preserve,
|
||||||
update_password)
|
update_password)
|
||||||
|
certmapdata = convert_certmapdata(certmapdata)
|
||||||
|
|
||||||
# Extend email addresses
|
# Extend email addresses
|
||||||
|
|
||||||
@@ -1001,6 +1052,16 @@ def main():
|
|||||||
msg="The use of passwordexpiration is not supported by "
|
msg="The use of passwordexpiration is not supported by "
|
||||||
"your IPA version")
|
"your IPA version")
|
||||||
|
|
||||||
|
# Check certmapdata availability.
|
||||||
|
# We need the connected API for this test, therefore it can not
|
||||||
|
# be part of check_parameters as this is used also before the
|
||||||
|
# connection to the API has been established.
|
||||||
|
if certmapdata is not None and \
|
||||||
|
not api_check_command("user_add_certmapdata"):
|
||||||
|
ansible_module.fail_json(
|
||||||
|
msg="The use of certmapdata is not supported by "
|
||||||
|
"your IPA version")
|
||||||
|
|
||||||
# Make sure user exists
|
# Make sure user exists
|
||||||
res_find = find_user(ansible_module, name)
|
res_find = find_user(ansible_module, name)
|
||||||
# Also search for preserved user if the user could not be found
|
# Also search for preserved user if the user could not be found
|
||||||
@@ -1082,7 +1143,7 @@ def main():
|
|||||||
certificate, res_find.get("usercertificate"))
|
certificate, res_find.get("usercertificate"))
|
||||||
|
|
||||||
certmapdata_add, certmapdata_del = gen_add_del_lists(
|
certmapdata_add, certmapdata_del = gen_add_del_lists(
|
||||||
certmapdata, res_find.get("ipaCertMapData"))
|
certmapdata, res_find.get("ipacertmapdata"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Use given managers and principals
|
# Use given managers and principals
|
||||||
@@ -1169,7 +1230,7 @@ def main():
|
|||||||
# Remove certmapdata
|
# Remove certmapdata
|
||||||
if len(certmapdata_del) > 0:
|
if len(certmapdata_del) > 0:
|
||||||
for _data in certmapdata_del:
|
for _data in certmapdata_del:
|
||||||
commands.append([name, "user_add_certmapdata",
|
commands.append([name, "user_remove_certmapdata",
|
||||||
gen_certmapdata_args(_data)])
|
gen_certmapdata_args(_data)])
|
||||||
|
|
||||||
elif action == "member":
|
elif action == "member":
|
||||||
|
|||||||
@@ -126,8 +126,6 @@
|
|||||||
certmapdata:
|
certmapdata:
|
||||||
- issuer: CN=issuer1
|
- issuer: CN=issuer1
|
||||||
subject: CN=subject1
|
subject: CN=subject1
|
||||||
- issuer: CN=issuer2
|
|
||||||
subject: CN=subject2
|
|
||||||
- issuer: CN=issuer3
|
- issuer: CN=issuer3
|
||||||
subject: CN=subject3
|
subject: CN=subject3
|
||||||
action: member
|
action: member
|
||||||
@@ -142,8 +140,6 @@
|
|||||||
certmapdata:
|
certmapdata:
|
||||||
- issuer: CN=issuer1
|
- issuer: CN=issuer1
|
||||||
subject: CN=subject1
|
subject: CN=subject1
|
||||||
- issuer: CN=issuer2
|
|
||||||
subject: CN=subject2
|
|
||||||
- issuer: CN=issuer3
|
- issuer: CN=issuer3
|
||||||
subject: CN=subject3
|
subject: CN=subject3
|
||||||
action: member
|
action: member
|
||||||
@@ -151,6 +147,85 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: result.changed
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata members absent
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- issuer: CN=issuer2
|
||||||
|
subject: CN=subject2
|
||||||
|
action: member
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata members absent again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- issuer: CN=issuer2
|
||||||
|
subject: CN=subject2
|
||||||
|
action: member
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata member present
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- issuer: CN=ca,dc=example,dc=com
|
||||||
|
subject: CN=test,dc=example,dc=com
|
||||||
|
action: member
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata member present again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- issuer: CN=ca,dc=example,dc=com
|
||||||
|
subject: CN=test,dc=example,dc=com
|
||||||
|
action: member
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata member (data) present again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- data: X509:<I>dc=com,dc=example,CN=ca<S>dc=com,dc=example,CN=test
|
||||||
|
action: member
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata member absent
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- issuer: CN=ca,dc=example,dc=com
|
||||||
|
subject: CN=test,dc=example,dc=com
|
||||||
|
action: member
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: User test certmapdata member (data) absent again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: test
|
||||||
|
certmapdata:
|
||||||
|
- data: X509:<I>dc=com,dc=example,CN=ca<S>dc=com,dc=example,CN=test
|
||||||
|
action: member
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
- name: User test absent
|
- name: User test absent
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
|||||||
Reference in New Issue
Block a user