1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/ipa_pwpolicy.py
patchback[bot] 4776e7bed3
[PR #7723/6afe35d2 backport][stable-8] ipa: ipa_pwpolicy update pwpolicy module (#7801)
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

(cherry picked from commit 6afe35d263)

Co-authored-by: Parsa Yousefi <p.yousefi97@gmail.com>
2023-12-31 15:43:52 +01:00

307 lines
11 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
---
module: ipa_pwpolicy
author: Adralioh (@adralioh)
short_description: Manage FreeIPA password policies
description:
- Add, modify, or delete a password policy using the IPA API.
version_added: 2.0.0
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
group:
description:
- Name of the group that the policy applies to.
- If omitted, the global policy is used.
aliases: ["name"]
type: str
state:
description: State to ensure.
default: "present"
choices: ["absent", "present"]
type: str
maxpwdlife:
description: Maximum password lifetime (in days).
type: str
minpwdlife:
description: Minimum password lifetime (in hours).
type: str
historylength:
description:
- Number of previous passwords that are remembered.
- Users cannot reuse remembered passwords.
type: str
minclasses:
description: Minimum number of character classes.
type: str
minlength:
description: Minimum password length.
type: str
priority:
description:
- Priority of the policy.
- High number means lower priority.
- Required when C(cn) is not the global policy.
type: str
maxfailcount:
description: Maximum number of consecutive failures before lockout.
type: str
failinterval:
description: Period (in seconds) after which the number of failed login attempts is reset.
type: str
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
'''
EXAMPLES = r'''
- name: Modify the global password policy
community.general.ipa_pwpolicy:
maxpwdlife: '90'
minpwdlife: '1'
historylength: '8'
minclasses: '3'
minlength: '16'
maxfailcount: '6'
failinterval: '60'
lockouttime: '600'
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
- name: Ensure the password policy for the group admins is present
community.general.ipa_pwpolicy:
group: admins
state: present
maxpwdlife: '60'
minpwdlife: '24'
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
- name: Ensure that the group sysops does not have a unique password policy
community.general.ipa_pwpolicy:
group: sysops
state: absent
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
'''
RETURN = r'''
pwpolicy:
description: Password policy as returned by IPA API.
returned: always
type: dict
sample:
cn: ['admins']
cospriority: ['10']
dn: 'cn=admins,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com'
krbmaxpwdlife: ['60']
krbminpwdlife: ['24']
krbpwdfailurecountinterval: ['600']
krbpwdhistorylength: ['16']
krbpwdlockoutduration: ['1200']
krbpwdmaxfailure: ['4']
krbpwdmindiffchars: ['4']
objectclass: ['top', 'nscontainer', 'krbpwdpolicy']
'''
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.ipa import IPAClient, ipa_argument_spec
from ansible.module_utils.common.text.converters import to_native
class PwPolicyIPAClient(IPAClient):
'''The global policy will be selected when `name` is `None`'''
def __init__(self, module, host, port, protocol):
super(PwPolicyIPAClient, self).__init__(module, host, port, protocol)
def pwpolicy_find(self, name):
if name is None:
# Manually set the cn to the global policy because pwpolicy_find will return a random
# different policy if cn is `None`
name = 'global_policy'
return self._post_json(method='pwpolicy_find', name=None, item={'all': True, 'cn': name})
def pwpolicy_add(self, name, item):
return self._post_json(method='pwpolicy_add', name=name, item=item)
def pwpolicy_mod(self, name, item):
return self._post_json(method='pwpolicy_mod', name=name, item=item)
def pwpolicy_del(self, name):
return self._post_json(method='pwpolicy_del', name=name)
def get_pwpolicy_dict(maxpwdlife=None, minpwdlife=None, historylength=None, minclasses=None,
minlength=None, priority=None, maxfailcount=None, failinterval=None,
lockouttime=None, gracelimit=None, maxrepeat=None, maxsequence=None, dictcheck=None, usercheck=None):
pwpolicy = {}
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
def get_pwpolicy_diff(client, ipa_pwpolicy, module_pwpolicy):
return client.get_diff(ipa_data=ipa_pwpolicy, module_data=module_pwpolicy)
def ensure(module, client):
state = module.params['state']
name = module.params['group']
module_pwpolicy = get_pwpolicy_dict(maxpwdlife=module.params.get('maxpwdlife'),
minpwdlife=module.params.get('minpwdlife'),
historylength=module.params.get('historylength'),
minclasses=module.params.get('minclasses'),
minlength=module.params.get('minlength'),
priority=module.params.get('priority'),
maxfailcount=module.params.get('maxfailcount'),
failinterval=module.params.get('failinterval'),
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)
changed = False
if state == 'present':
if not ipa_pwpolicy:
changed = True
if not module.check_mode:
ipa_pwpolicy = client.pwpolicy_add(name=name, item=module_pwpolicy)
else:
diff = get_pwpolicy_diff(client, ipa_pwpolicy, module_pwpolicy)
if len(diff) > 0:
changed = True
if not module.check_mode:
ipa_pwpolicy = client.pwpolicy_mod(name=name, item=module_pwpolicy)
else:
if ipa_pwpolicy:
changed = True
if not module.check_mode:
client.pwpolicy_del(name=name)
return changed, ipa_pwpolicy
def main():
argument_spec = ipa_argument_spec()
argument_spec.update(group=dict(type='str', aliases=['name']),
state=dict(type='str', default='present', choices=['present', 'absent']),
maxpwdlife=dict(type='str'),
minpwdlife=dict(type='str'),
historylength=dict(type='str'),
minclasses=dict(type='str'),
minlength=dict(type='str'),
priority=dict(type='str'),
maxfailcount=dict(type='str'),
failinterval=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)
client = PwPolicyIPAClient(module=module,
host=module.params['ipa_host'],
port=module.params['ipa_port'],
protocol=module.params['ipa_prot'])
try:
client.login(username=module.params['ipa_user'],
password=module.params['ipa_pass'])
changed, pwpolicy = ensure(module, client)
except Exception as e:
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
module.exit_json(changed=changed, pwpolicy=pwpolicy)
if __name__ == '__main__':
main()