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] info[1] = self.user_password()[0]
return info return info
def set_password_expire_max(self): def set_password_expire(self):
command_name = 'chage' min_needs_change = self.password_expire_min is not None
cmd = [self.module.get_bin_path(command_name, True)] max_needs_change = self.password_expire_max is not None
cmd.append('-M')
cmd.append(self.password_expire_max) if HAVE_SPWD:
cmd.append(self.name) shadow_info = spwd.getspnam(self.name)
if self.password_expire_max == spwd.getspnam(self.name).sp_max: min_needs_change &= self.password_expire_min != shadow_info.sp_min
self.module.exit_json(changed=False) max_needs_change &= self.password_expire_max != shadow_info.sp_max
else:
self.execute_command(cmd) if not (min_needs_change or max_needs_change):
self.module.exit_json(changed=True) return (None, '', '') # target state already reached
def set_password_expire_min(self):
command_name = 'chage' command_name = 'chage'
cmd = [self.module.get_bin_path(command_name, True)] cmd = [self.module.get_bin_path(command_name, True)]
cmd.append('-m') if min_needs_change:
cmd.append(self.password_expire_min) cmd.extend(["-m", self.password_expire_min])
if max_needs_change:
cmd.extend(["-M", self.password_expire_max])
cmd.append(self.name) cmd.append(self.name)
if self.password_expire_min == spwd.getspnam(self.name).sp_min:
self.module.exit_json(changed=False) return self.execute_command(cmd)
else:
self.execute_command(cmd)
self.module.exit_json(changed=True)
def user_password(self): def user_password(self):
passwd = '' passwd = ''
@ -3207,15 +3205,14 @@ def main():
result['ssh_key_file'] = user.get_ssh_key_path() result['ssh_key_file'] = user.get_ssh_key_path()
result['ssh_public_key'] = user.get_ssh_public_key() result['ssh_public_key'] = user.get_ssh_public_key()
# deal with password expire max (rc, out, err) = user.set_password_expire()
if user.password_expire_max: if rc is None:
if user.user_exists(): pass # target state reached, nothing to do
user.set_password_expire_max() else:
if rc != 0:
# deal with password expire min module.fail_json(name=user.name, msg=err, rc=rc)
if user.password_expire_min: else:
if user.user_exists(): result['changed'] = True
user.set_password_expire_min()
module.exit_json(**result) module.exit_json(**result)

@ -21,6 +21,11 @@
meta: end_host meta: end_host
when: ansible_distribution == 'Alpine' 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_user.yml
- import_tasks: test_create_system_user.yml - import_tasks: test_create_system_user.yml
- import_tasks: test_create_user_uid.yml - import_tasks: test_create_user_uid.yml

@ -53,3 +53,21 @@
that: that:
- ansible_facts.getent_shadow['ansibulluser'][2] == '5' - ansible_facts.getent_shadow['ansibulluser'][2] == '5'
- ansible_facts.getent_shadow['ansibulluser'][3] == '10' - 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