From 6afe35d26345eb2cb3a232c0ee935bee7cc6b009 Mon Sep 17 00:00:00 2001
From: Parsa Yousefi
Date: Sun, 31 Dec 2023 17:51:59 +0330
Subject: [PATCH] ipa: ipa_pwpolicy update pwpolicy module (#7723)
* ipa: ipa_pwpolicy support maxrepeat, maxsequence, dictcheck, usercheck, gracelimit
* ipa: ipa_pwdpolicy replace if statements with for loop
* ipa: ipa_pwdpolicy add changelog
---
...23-ipa-pwpolicy-update-pwpolicy-module.yml | 3 +
plugins/modules/ipa_pwpolicy.py | 89 ++++++++++---
.../unit/plugins/modules/test_ipa_pwpolicy.py | 123 ++++++++++++++++--
3 files changed, 180 insertions(+), 35 deletions(-)
create mode 100644 changelogs/fragments/7723-ipa-pwpolicy-update-pwpolicy-module.yml
diff --git a/changelogs/fragments/7723-ipa-pwpolicy-update-pwpolicy-module.yml b/changelogs/fragments/7723-ipa-pwpolicy-update-pwpolicy-module.yml
new file mode 100644
index 0000000000..bffd40efcd
--- /dev/null
+++ b/changelogs/fragments/7723-ipa-pwpolicy-update-pwpolicy-module.yml
@@ -0,0 +1,3 @@
+minor_changes:
+ - ipa_pwpolicy - update module to support ``maxrepeat``, ``maxsequence``, ``dictcheck``, ``usercheck``, ``gracelimit`` parameters in FreeIPA password policies (https://github.com/ansible-collections/community.general/pull/7723).
+ - ipa_pwpolicy - refactor module and exchange a sequence ``if`` statements with a ``for`` loop (https://github.com/ansible-collections/community.general/pull/7723).
diff --git a/plugins/modules/ipa_pwpolicy.py b/plugins/modules/ipa_pwpolicy.py
index 6a6c4318ba..ba7d702916 100644
--- a/plugins/modules/ipa_pwpolicy.py
+++ b/plugins/modules/ipa_pwpolicy.py
@@ -64,6 +64,26 @@ options:
lockouttime:
description: Period (in seconds) for which users are locked out.
type: str
+ gracelimit:
+ description: Maximum number of LDAP logins after password expiration.
+ type: int
+ version_added: 8.2.0
+ maxrepeat:
+ description: Maximum number of allowed same consecutive characters in the new password.
+ type: int
+ version_added: 8.2.0
+ maxsequence:
+ description: Maximum length of monotonic character sequences in the new password. An example of a monotonic sequence of length 5 is V(12345).
+ type: int
+ version_added: 8.2.0
+ dictcheck:
+ description: Check whether the password (with possible modifications) matches a word in a dictionary (using cracklib).
+ type: bool
+ version_added: 8.2.0
+ usercheck:
+ description: Check whether the password (with possible modifications) contains the user name in some form (if the name has > 3 characters).
+ type: bool
+ version_added: 8.2.0
extends_documentation_fragment:
- community.general.ipa.documentation
- community.general.attributes
@@ -93,9 +113,15 @@ EXAMPLES = r'''
historylength: '16'
minclasses: '4'
priority: '10'
+ minlength: '6'
maxfailcount: '4'
failinterval: '600'
lockouttime: '1200'
+ gracelimit: 3
+ maxrepeat: 3
+ maxsequence: 3
+ dictcheck: true
+ usercheck: true
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
@@ -159,26 +185,35 @@ class PwPolicyIPAClient(IPAClient):
def get_pwpolicy_dict(maxpwdlife=None, minpwdlife=None, historylength=None, minclasses=None,
minlength=None, priority=None, maxfailcount=None, failinterval=None,
- lockouttime=None):
+ lockouttime=None, gracelimit=None, maxrepeat=None, maxsequence=None, dictcheck=None, usercheck=None):
pwpolicy = {}
- if maxpwdlife is not None:
- pwpolicy['krbmaxpwdlife'] = maxpwdlife
- if minpwdlife is not None:
- pwpolicy['krbminpwdlife'] = minpwdlife
- if historylength is not None:
- pwpolicy['krbpwdhistorylength'] = historylength
- if minclasses is not None:
- pwpolicy['krbpwdmindiffchars'] = minclasses
- if minlength is not None:
- pwpolicy['krbpwdminlength'] = minlength
- if priority is not None:
- pwpolicy['cospriority'] = priority
- if maxfailcount is not None:
- pwpolicy['krbpwdmaxfailure'] = maxfailcount
- if failinterval is not None:
- pwpolicy['krbpwdfailurecountinterval'] = failinterval
- if lockouttime is not None:
- pwpolicy['krbpwdlockoutduration'] = lockouttime
+ pwpolicy_options = {
+ 'krbmaxpwdlife': maxpwdlife,
+ 'krbminpwdlife': minpwdlife,
+ 'krbpwdhistorylength': historylength,
+ 'krbpwdmindiffchars': minclasses,
+ 'krbpwdminlength': minlength,
+ 'cospriority': priority,
+ 'krbpwdmaxfailure': maxfailcount,
+ 'krbpwdfailurecountinterval': failinterval,
+ 'krbpwdlockoutduration': lockouttime,
+ 'passwordgracelimit': gracelimit,
+ 'ipapwdmaxrepeat': maxrepeat,
+ 'ipapwdmaxsequence': maxsequence,
+ }
+
+ pwpolicy_boolean_options = {
+ 'ipapwddictcheck': dictcheck,
+ 'ipapwdusercheck': usercheck,
+ }
+
+ for option, value in pwpolicy_options.items():
+ if value is not None:
+ pwpolicy[option] = to_native(value)
+
+ for option, value in pwpolicy_boolean_options.items():
+ if value is not None:
+ pwpolicy[option] = bool(value)
return pwpolicy
@@ -199,7 +234,13 @@ def ensure(module, client):
priority=module.params.get('priority'),
maxfailcount=module.params.get('maxfailcount'),
failinterval=module.params.get('failinterval'),
- lockouttime=module.params.get('lockouttime'))
+ lockouttime=module.params.get('lockouttime'),
+ gracelimit=module.params.get('gracelimit'),
+ maxrepeat=module.params.get('maxrepeat'),
+ maxsequence=module.params.get('maxsequence'),
+ dictcheck=module.params.get('dictcheck'),
+ usercheck=module.params.get('usercheck'),
+ )
ipa_pwpolicy = client.pwpolicy_find(name=name)
@@ -236,7 +277,13 @@ def main():
priority=dict(type='str'),
maxfailcount=dict(type='str'),
failinterval=dict(type='str'),
- lockouttime=dict(type='str'))
+ lockouttime=dict(type='str'),
+ gracelimit=dict(type='int'),
+ maxrepeat=dict(type='int'),
+ maxsequence=dict(type='int'),
+ dictcheck=dict(type='bool'),
+ usercheck=dict(type='bool'),
+ )
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
diff --git a/tests/unit/plugins/modules/test_ipa_pwpolicy.py b/tests/unit/plugins/modules/test_ipa_pwpolicy.py
index b45c566fc2..538f61e9aa 100644
--- a/tests/unit/plugins/modules/test_ipa_pwpolicy.py
+++ b/tests/unit/plugins/modules/test_ipa_pwpolicy.py
@@ -100,7 +100,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {}
mock_calls = (
@@ -124,7 +129,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -145,7 +155,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {}
mock_calls = (
@@ -169,7 +184,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -190,7 +210,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '12',
'maxfailcount': '8',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['sysops'],
@@ -203,6 +228,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -227,7 +257,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '12',
'krbpwdmaxfailure': '8',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -248,7 +283,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['sysops'],
@@ -281,7 +321,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -342,7 +387,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['admins'],
@@ -355,6 +405,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=admins,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -409,7 +464,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '12',
'maxfailcount': '8',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['global_policy'],
@@ -420,6 +480,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -443,7 +508,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '12',
'krbpwdmaxfailure': '8',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -461,7 +531,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['global_policy'],
@@ -473,6 +548,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -504,7 +584,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {}
mock_calls = [
@@ -535,7 +620,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '12',
'maxfailcount': '8',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['sysops'],
@@ -548,6 +638,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}