summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changelogs/fragments/69531_user_password_expire.yml2
-rw-r--r--lib/ansible/modules/user.py71
-rw-r--r--test/integration/targets/user/tasks/main.yml1
-rw-r--r--test/integration/targets/user/tasks/test_expires_min_max.yml55
4 files changed, 129 insertions, 0 deletions
diff --git a/changelogs/fragments/69531_user_password_expire.yml b/changelogs/fragments/69531_user_password_expire.yml
new file mode 100644
index 0000000000..1ac2c0a931
--- /dev/null
+++ b/changelogs/fragments/69531_user_password_expire.yml
@@ -0,0 +1,2 @@
+minor_changes:
+ - user - add new parameters ``password_expire_max`` and ``password_expire_min`` for controlling password expiration (https://github.com/ansible/ansible/issues/68775)
diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py
index 4abc74dc58..5e43d05191 100644
--- a/lib/ansible/modules/user.py
+++ b/lib/ansible/modules/user.py
@@ -238,6 +238,19 @@ options:
- Currently supported on Illumos/Solaris.
type: str
version_added: "2.8"
+ password_expire_max:
+ description:
+ - Maximum number of days between password change.
+ - Supported on Linux only.
+ type: int
+ version_added: "2.11"
+ password_expire_min:
+ description:
+ - Minimum number of days between password change.
+ - Supported on Linux only.
+ type: int
+ version_added: "2.11"
+
notes:
- There are specific requirements per platform on user management utilities. However
they generally come pre-installed with the system and Ansible will require they
@@ -299,6 +312,16 @@ EXAMPLES = r'''
ansible.builtin.user:
name: james18
expires: -1
+
+- name: Set maximum expiration date for password
+ user:
+ name: ram19
+ password_expire_max: 10
+
+- name: Set minimum expiration date for password
+ user:
+ name: pushkar15
+ password_expire_min: 5
'''
RETURN = r'''
@@ -400,6 +423,16 @@ uid:
returned: When I(uid) is passed to the module
type: int
sample: 1044
+password_expire_max:
+ description: Maximum number of days during which a password is valid.
+ returned: When user exists
+ type: int
+ sample: 20
+password_expire_min:
+ description: Minimum number of days between password change
+ returned: When user exists
+ type: int
+ sample: 20
'''
@@ -494,6 +527,8 @@ class User(object):
self.profile = module.params['profile']
self.authorization = module.params['authorization']
self.role = module.params['role']
+ self.password_expire_max = module.params['password_expire_max']
+ self.password_expire_min = module.params['password_expire_min']
if module.params['groups'] is not None:
self.groups = ','.join(module.params['groups'])
@@ -989,6 +1024,30 @@ 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_min(self):
+ command_name = 'chage'
+ cmd = [self.module.get_bin_path(command_name, True)]
+ cmd.append('-m')
+ cmd.append(self.password_expire_min)
+ 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)
+
def user_password(self):
passwd = ''
expires = ''
@@ -2957,6 +3016,8 @@ def main():
shell=dict(type='str'),
password=dict(type='str', no_log=True),
login_class=dict(type='str'),
+ password_expire_max=dict(type='int', no_log=False),
+ password_expire_min=dict(type='int', no_log=False),
# following options are specific to macOS
hidden=dict(type='bool'),
# following options are specific to selinux
@@ -3096,6 +3157,16 @@ 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():
+ (rc, out, err) = user.set_password_expire_max()
+
+ # deal with password expire min
+ if user.password_expire_min:
+ if user.user_exists():
+ (rc, out, err) = user.set_password_expire_min()
+
module.exit_json(**result)
diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml
index 3979d5ce80..d3bae05644 100644
--- a/test/integration/targets/user/tasks/main.yml
+++ b/test/integration/targets/user/tasks/main.yml
@@ -31,6 +31,7 @@
- import_tasks: test_expires.yml
- import_tasks: test_expires_new_account.yml
- import_tasks: test_expires_new_account_epoch_negative.yml
+- import_tasks: test_expires_min_max.yml
- import_tasks: test_shadow_backup.yml
- import_tasks: test_ssh_key_passphrase.yml
- import_tasks: test_password_lock.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
new file mode 100644
index 0000000000..80e607b6fc
--- /dev/null
+++ b/test/integration/targets/user/tasks/test_expires_min_max.yml
@@ -0,0 +1,55 @@
+# https://github.com/ansible/ansible/issues/68775
+- name: Test setting maximum expiration
+ when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse']
+ block:
+ - name: create user
+ user:
+ name: ansibulluser
+ state: present
+
+ - name: add maximum expire date for password
+ user:
+ name: ansibulluser
+ password_expire_max: 10
+ register: pass_max_1_0
+
+ - name: again add maximum expire date for password
+ user:
+ name: ansibulluser
+ password_expire_max: 10
+ register: pass_max_1_1
+
+ - name: validate result for maximum expire date
+ assert:
+ that:
+ - pass_max_1_0 is changed
+ - pass_max_1_1 is not changed
+
+ - name: add minimum expire date for password
+ user:
+ name: ansibulluser
+ password_expire_min: 5
+ register: pass_min_2_0
+
+ - name: again add minimum expire date for password
+ user:
+ name: ansibulluser
+ password_expire_min: 5
+ register: pass_min_2_1
+
+ - name: validate result for minimum expire date
+ assert:
+ that:
+ - pass_min_2_0 is changed
+ - pass_min_2_1 is not changed
+
+ - 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] == '5'
+ - ansible_facts.getent_shadow['ansibulluser'][3] == '10'