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>
pull/76845/head
Daniel Goldman 4 years ago committed by GitHub
parent 5a69023941
commit dbde2c2ae3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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'

Loading…
Cancel
Save