From 54b2a5d4d8e3b01feb4bebdad292afb9485af852 Mon Sep 17 00:00:00 2001
From: Parsa
Date: Thu, 14 Dec 2023 14:40:35 +0330
Subject: [PATCH 1/3] ipa: ipa_pwpolicy support maxrepeat, maxsequence,
dictcheck, usercheck, gracelimit
---
plugins/modules/ipa_pwpolicy.py | 58 ++++++++-
.../unit/plugins/modules/test_ipa_pwpolicy.py | 123 ++++++++++++++++--
2 files changed, 164 insertions(+), 17 deletions(-)
diff --git a/plugins/modules/ipa_pwpolicy.py b/plugins/modules/ipa_pwpolicy.py
index 6a6c4318ba8..aa5630ca583 100644
--- a/plugins/modules/ipa_pwpolicy.py
+++ b/plugins/modules/ipa_pwpolicy.py
@@ -64,6 +64,26 @@
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 @@
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,7 +185,7 @@ def pwpolicy_del(self, name):
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
@@ -179,6 +205,20 @@ def get_pwpolicy_dict(maxpwdlife=None, minpwdlife=None, historylength=None, minc
pwpolicy['krbpwdfailurecountinterval'] = failinterval
if lockouttime is not None:
pwpolicy['krbpwdlockoutduration'] = lockouttime
+ if gracelimit is not None:
+ pwpolicy['passwordgracelimit'] = str(gracelimit)
+ if maxrepeat is not None:
+ pwpolicy['ipapwdmaxrepeat'] = str(maxrepeat)
+ if maxsequence is not None:
+ pwpolicy['ipapwdmaxsequence'] = str(maxsequence)
+ if dictcheck is True:
+ pwpolicy['ipapwddictcheck'] = True
+ if dictcheck is False:
+ pwpolicy['ipapwddictcheck'] = False
+ if usercheck is True:
+ pwpolicy['ipapwdusercheck'] = True
+ if usercheck is False:
+ pwpolicy['ipapwdusercheck'] = False
return pwpolicy
@@ -199,7 +239,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 +282,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 b45c566fc20..538f61e9aae 100644
--- a/tests/unit/plugins/modules/test_ipa_pwpolicy.py
+++ b/tests/unit/plugins/modules/test_ipa_pwpolicy.py
@@ -100,7 +100,12 @@ def test_add(self):
'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 @@ def test_add(self):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -145,7 +155,12 @@ def test_aliases(self):
'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 @@ def test_aliases(self):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -190,7 +210,12 @@ def test_mod_different_args(self):
'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 @@ def test_mod_different_args(self):
'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 @@ def test_mod_different_args(self):
'krbpwdminlength': '12',
'krbpwdmaxfailure': '8',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -248,7 +283,12 @@ def test_mod_missing_args(self):
'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 @@ def test_mod_missing_args(self):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -342,7 +387,12 @@ def test_no_change(self):
'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 @@ def test_no_change(self):
'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 @@ def test_global(self):
'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 @@ def test_global(self):
'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 @@ def test_global(self):
'krbpwdminlength': '12',
'krbpwdmaxfailure': '8',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -461,7 +531,12 @@ def test_global_no_change(self):
'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 @@ def test_global_no_change(self):
'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 @@ def test_check_add(self):
'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 @@ def test_check_mod(self):
'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 @@ def test_check_mod(self):
'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']
}
From 07010457e84d50d9a11b145b1062f00ec8193d88 Mon Sep 17 00:00:00 2001
From: Parsa
Date: Fri, 15 Dec 2023 00:57:21 +0330
Subject: [PATCH 2/3] ipa: ipa_pwdpolicy replace if statements with for loop
---
plugins/modules/ipa_pwpolicy.py | 59 +++++++++++++++------------------
1 file changed, 27 insertions(+), 32 deletions(-)
diff --git a/plugins/modules/ipa_pwpolicy.py b/plugins/modules/ipa_pwpolicy.py
index aa5630ca583..ba7d702916b 100644
--- a/plugins/modules/ipa_pwpolicy.py
+++ b/plugins/modules/ipa_pwpolicy.py
@@ -187,38 +187,33 @@ def get_pwpolicy_dict(maxpwdlife=None, minpwdlife=None, historylength=None, minc
minlength=None, priority=None, maxfailcount=None, failinterval=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
- if gracelimit is not None:
- pwpolicy['passwordgracelimit'] = str(gracelimit)
- if maxrepeat is not None:
- pwpolicy['ipapwdmaxrepeat'] = str(maxrepeat)
- if maxsequence is not None:
- pwpolicy['ipapwdmaxsequence'] = str(maxsequence)
- if dictcheck is True:
- pwpolicy['ipapwddictcheck'] = True
- if dictcheck is False:
- pwpolicy['ipapwddictcheck'] = False
- if usercheck is True:
- pwpolicy['ipapwdusercheck'] = True
- if usercheck is False:
- pwpolicy['ipapwdusercheck'] = False
+ 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
From adec4bd604fc43bb2291b32be767a3c0d1c4ee8d Mon Sep 17 00:00:00 2001
From: Parsa
Date: Fri, 15 Dec 2023 13:18:49 +0330
Subject: [PATCH 3/3] ipa: ipa_pwdpolicy add changelog
---
.../fragments/7723-ipa-pwpolicy-update-pwpolicy-module.yml | 3 +++
1 file changed, 3 insertions(+)
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 00000000000..bffd40efcdc
--- /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).