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'] }