diff options
author | Daniel Goldman <merkavabuilder@gmail.com> | 2022-01-24 20:52:38 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-24 20:52:38 +0100 |
commit | dbde2c2ae3b03469abbe8f2c98b50ffedcf7975f (patch) | |
tree | 30568ce73a9e016ee84279bacaf478de0694baf2 | |
parent | remove ansibot from collection docs (#76817) (diff) | |
download | ansible-dbde2c2ae3b03469abbe8f2c98b50ffedcf7975f.tar.xz ansible-dbde2c2ae3b03469abbe8f2c98b50ffedcf7975f.zip |
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>
-rw-r--r-- | changelogs/fragments/75017-user-password-expiry.yml | 3 | ||||
-rw-r--r-- | lib/ansible/modules/user.py | 53 | ||||
-rw-r--r-- | test/integration/targets/user/tasks/main.yml | 5 | ||||
-rw-r--r-- | test/integration/targets/user/tasks/test_expires_min_max.yml | 18 |
4 files changed, 51 insertions, 28 deletions
diff --git a/changelogs/fragments/75017-user-password-expiry.yml b/changelogs/fragments/75017-user-password-expiry.yml new file mode 100644 index 0000000000..b264f07f74 --- /dev/null +++ b/changelogs/fragments/75017-user-password-expiry.yml @@ -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) diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py index 62b7a6fd69..3c089ddf00 100644 --- a/lib/ansible/modules/user.py +++ b/lib/ansible/modules/user.py @@ -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) diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml index 5e1d2d220d..3807831f03 100644 --- a/test/integration/targets/user/tasks/main.yml +++ b/test/integration/targets/user/tasks/main.yml @@ -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 diff --git a/test/integration/targets/user/tasks/test_expires_min_max.yml b/test/integration/targets/user/tasks/test_expires_min_max.yml index 80e607b6fc..0b8037918d 100644 --- a/test/integration/targets/user/tasks/test_expires_min_max.yml +++ b/test/integration/targets/user/tasks/test_expires_min_max.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' |