user module password expiration fixes (#75390)
* allow inputting 0 for password_expire_{min|max}
0 is meaningful for min days (any time)
0 is technically valid for max_days
* add test for setting both min and max expiry
* [0] return result of execute_command from set_password_expire*
* [1] better return for set_password_expire
* [2] handle returns from set_password_expire*
* only set password expiry if user exists
* collect return-handling code
* combine password min and max into one execution
* handle case where spwd is not present like on macOS and FreeBSD
Co-authored-by: Sam Doran <sdoran@redhat.com>
This commit is contained in:
3
changelogs/fragments/75017-user-password-expiry.yml
Normal file
3
changelogs/fragments/75017-user-password-expiry.yml
Normal file
@ -0,0 +1,3 @@
|
||||
bugfixes:
|
||||
- user - allow ``password_expiry_min`` and ``password_expiry_min`` to be set to ``0`` (https://github.com/ansible/ansible/issues/75017)
|
||||
- user - allow password min and max to be set at the same time (https://github.com/ansible/ansible/issues/75017)
|
||||
@ -1048,29 +1048,27 @@ class User(object):
|
||||
info[1] = self.user_password()[0]
|
||||
return info
|
||||
|
||||
def set_password_expire_max(self):
|
||||
command_name = 'chage'
|
||||
cmd = [self.module.get_bin_path(command_name, True)]
|
||||
cmd.append('-M')
|
||||
cmd.append(self.password_expire_max)
|
||||
cmd.append(self.name)
|
||||
if self.password_expire_max == spwd.getspnam(self.name).sp_max:
|
||||
self.module.exit_json(changed=False)
|
||||
else:
|
||||
self.execute_command(cmd)
|
||||
self.module.exit_json(changed=True)
|
||||
def set_password_expire(self):
|
||||
min_needs_change = self.password_expire_min is not None
|
||||
max_needs_change = self.password_expire_max is not None
|
||||
|
||||
if HAVE_SPWD:
|
||||
shadow_info = spwd.getspnam(self.name)
|
||||
min_needs_change &= self.password_expire_min != shadow_info.sp_min
|
||||
max_needs_change &= self.password_expire_max != shadow_info.sp_max
|
||||
|
||||
if not (min_needs_change or max_needs_change):
|
||||
return (None, '', '') # target state already reached
|
||||
|
||||
def set_password_expire_min(self):
|
||||
command_name = 'chage'
|
||||
cmd = [self.module.get_bin_path(command_name, True)]
|
||||
cmd.append('-m')
|
||||
cmd.append(self.password_expire_min)
|
||||
if min_needs_change:
|
||||
cmd.extend(["-m", self.password_expire_min])
|
||||
if max_needs_change:
|
||||
cmd.extend(["-M", self.password_expire_max])
|
||||
cmd.append(self.name)
|
||||
if self.password_expire_min == spwd.getspnam(self.name).sp_min:
|
||||
self.module.exit_json(changed=False)
|
||||
else:
|
||||
self.execute_command(cmd)
|
||||
self.module.exit_json(changed=True)
|
||||
|
||||
return self.execute_command(cmd)
|
||||
|
||||
def user_password(self):
|
||||
passwd = ''
|
||||
@ -3207,15 +3205,14 @@ def main():
|
||||
result['ssh_key_file'] = user.get_ssh_key_path()
|
||||
result['ssh_public_key'] = user.get_ssh_public_key()
|
||||
|
||||
# deal with password expire max
|
||||
if user.password_expire_max:
|
||||
if user.user_exists():
|
||||
user.set_password_expire_max()
|
||||
|
||||
# deal with password expire min
|
||||
if user.password_expire_min:
|
||||
if user.user_exists():
|
||||
user.set_password_expire_min()
|
||||
(rc, out, err) = user.set_password_expire()
|
||||
if rc is None:
|
||||
pass # target state reached, nothing to do
|
||||
else:
|
||||
if rc != 0:
|
||||
module.fail_json(name=user.name, msg=err, rc=rc)
|
||||
else:
|
||||
result['changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@ -21,6 +21,11 @@
|
||||
meta: end_host
|
||||
when: ansible_distribution == 'Alpine'
|
||||
|
||||
- name: ensure output directory exists
|
||||
file:
|
||||
dest: "{{ output_dir }}"
|
||||
state: directory
|
||||
|
||||
- import_tasks: test_create_user.yml
|
||||
- import_tasks: test_create_system_user.yml
|
||||
- import_tasks: test_create_user_uid.yml
|
||||
|
||||
@ -53,3 +53,21 @@
|
||||
that:
|
||||
- ansible_facts.getent_shadow['ansibulluser'][2] == '5'
|
||||
- ansible_facts.getent_shadow['ansibulluser'][3] == '10'
|
||||
|
||||
- name: Set min and max at the same time
|
||||
user:
|
||||
name: ansibulluser
|
||||
# also checks that assigning 0 works
|
||||
password_expire_min: 0
|
||||
password_expire_max: 0
|
||||
|
||||
- name: Get shadow data for ansibulluser
|
||||
getent:
|
||||
database: shadow
|
||||
key: ansibulluser
|
||||
|
||||
- name: Ensure password expiration was set properly
|
||||
assert:
|
||||
that:
|
||||
- ansible_facts.getent_shadow['ansibulluser'][2] == '0'
|
||||
- ansible_facts.getent_shadow['ansibulluser'][3] == '0'
|
||||
|
||||
Reference in New Issue
Block a user