diff options
Diffstat (limited to 'test/integration/targets/user')
21 files changed, 1597 insertions, 0 deletions
diff --git a/test/integration/targets/user/aliases b/test/integration/targets/user/aliases new file mode 100644 index 00000000..3a07aab3 --- /dev/null +++ b/test/integration/targets/user/aliases @@ -0,0 +1,3 @@ +destructive +shippable/posix/group1 +skip/aix diff --git a/test/integration/targets/user/files/userlist.sh b/test/integration/targets/user/files/userlist.sh new file mode 100644 index 00000000..96a83b20 --- /dev/null +++ b/test/integration/targets/user/files/userlist.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +#- name: make a list of groups +# shell: | +# cat /etc/group | cut -d: -f1 +# register: group_names +# when: 'ansible_distribution != "MacOSX"' + +#- name: make a list of groups [mac] +# shell: dscl localhost -list /Local/Default/Groups +# register: group_names +# when: 'ansible_distribution == "MacOSX"' + +DISTRO="$*" + +if [[ "$DISTRO" == "MacOSX" ]]; then + dscl localhost -list /Local/Default/Users +else + grep -E -v ^\# /etc/passwd | cut -d: -f1 +fi diff --git a/test/integration/targets/user/meta/main.yml b/test/integration/targets/user/meta/main.yml new file mode 100644 index 00000000..07faa217 --- /dev/null +++ b/test/integration/targets/user/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml new file mode 100644 index 00000000..3b8ff377 --- /dev/null +++ b/test/integration/targets/user/tasks/main.yml @@ -0,0 +1,34 @@ +# Test code for the user module. +# (c) 2017, James Tanner <tanner.jc@gmail.com> + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# + +- import_tasks: test_create_user.yml +- import_tasks: test_create_system_user.yml +- import_tasks: test_create_user_uid.yml +- import_tasks: test_create_user_password.yml +- import_tasks: test_create_user_home.yml +- import_tasks: test_remove_user.yml +- import_tasks: test_no_home_fallback.yml +- import_tasks: test_expires.yml +- import_tasks: test_expires_new_account.yml +- import_tasks: test_expires_new_account_epoch_negative.yml +- import_tasks: test_shadow_backup.yml +- import_tasks: test_ssh_key_passphrase.yml +- import_tasks: test_password_lock.yml +- import_tasks: test_password_lock_new_user.yml +- import_tasks: test_local.yml diff --git a/test/integration/targets/user/tasks/test_create_system_user.yml b/test/integration/targets/user/tasks/test_create_system_user.yml new file mode 100644 index 00000000..da746c50 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_system_user.yml @@ -0,0 +1,12 @@ +# create system user + +- name: remove user + user: + name: ansibulluser + state: absent + +- name: create system user + user: + name: ansibulluser + state: present + system: yes diff --git a/test/integration/targets/user/tasks/test_create_user.yml b/test/integration/targets/user/tasks/test_create_user.yml new file mode 100644 index 00000000..bced7905 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user.yml @@ -0,0 +1,67 @@ +- name: remove the test user + user: + name: ansibulluser + state: absent + +- name: try to create a user + user: + name: ansibulluser + state: present + register: user_test0_0 + +- name: create the user again + user: + name: ansibulluser + state: present + register: user_test0_1 + +- debug: + var: user_test0 + verbosity: 2 + +- name: make a list of users + script: userlist.sh {{ ansible_facts.distribution }} + register: user_names + +- debug: + var: user_names + verbosity: 2 + +- name: validate results for testcase 0 + assert: + that: + - user_test0_0 is changed + - user_test0_1 is not changed + - '"ansibulluser" in user_names.stdout_lines' + +- name: run existing user check tests + user: + name: "{{ user_names.stdout_lines | random }}" + state: present + create_home: no + loop: "{{ range(1, 5+1) | list }}" + register: user_test1 + +- debug: + var: user_test1 + verbosity: 2 + +- name: validate results for testcase 1 + assert: + that: + - user_test1.results is defined + - user_test1.results | length == 5 + +- name: validate changed results for testcase 1 + assert: + that: + - "user_test1.results[0] is not changed" + - "user_test1.results[1] is not changed" + - "user_test1.results[2] is not changed" + - "user_test1.results[3] is not changed" + - "user_test1.results[4] is not changed" + - "user_test1.results[0]['state'] == 'present'" + - "user_test1.results[1]['state'] == 'present'" + - "user_test1.results[2]['state'] == 'present'" + - "user_test1.results[3]['state'] == 'present'" + - "user_test1.results[4]['state'] == 'present'" diff --git a/test/integration/targets/user/tasks/test_create_user_home.yml b/test/integration/targets/user/tasks/test_create_user_home.yml new file mode 100644 index 00000000..1b529f76 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user_home.yml @@ -0,0 +1,136 @@ +# https://github.com/ansible/ansible/issues/42484 +# Skipping macOS for now since there is a bug when changing home directory +- name: Test home directory creation + when: ansible_facts.system != 'Darwin' + block: + - name: create user specifying home + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" + register: user_test3_0 + + - name: create user again specifying home + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" + register: user_test3_1 + + - name: change user home + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser-mod" + register: user_test3_2 + + - name: change user home back + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" + register: user_test3_3 + + - name: validate results for testcase 3 + assert: + that: + - user_test3_0 is not changed + - user_test3_1 is not changed + - user_test3_2 is changed + - user_test3_3 is changed + +# https://github.com/ansible/ansible/issues/41393 +# Create a new user account with a path that has parent directories that do not exist +- name: Create user with home path that has parents that do not exist + user: + name: ansibulluser2 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_with_no_parent_1 + +- name: Create user with home path that has parents that do not exist again + user: + name: ansibulluser2 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_with_no_parent_2 + +- name: Check the created home directory + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: home_with_no_parent_3 + +- name: Ensure user with non-existing parent paths was created successfully + assert: + that: + - create_home_with_no_parent_1 is changed + - create_home_with_no_parent_1.home == user_home_prefix[ansible_facts.system] ~ '/in2deep/ansibulluser2' + - create_home_with_no_parent_2 is not changed + - home_with_no_parent_3.stat.uid == create_home_with_no_parent_1.uid + - home_with_no_parent_3.stat.gr_name == default_user_group[ansible_facts.distribution] | default('ansibulluser2') + +- name: Cleanup test account + user: + name: ansibulluser2 + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + state: absent + remove: yes + +- name: Remove testing dir + file: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/" + state: absent + + +# https://github.com/ansible/ansible/issues/60307 +# Make sure we can create a user when the home directory is missing +- name: Create user with home path that does not exist + user: + name: ansibulluser3 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/nosuchdir" + createhome: no + +- name: Cleanup test account + user: + name: ansibulluser3 + state: absent + remove: yes + +# https://github.com/ansible/ansible/issues/70589 +# Create user with create_home: no and parent directory does not exist. +- name: "Check if parent dir for home dir for user exists (before)" + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir" + register: create_user_no_create_home_with_no_parent_parent_dir_before + +- name: "Create user with create_home == no and home path parent dir does not exist" + user: + name: randomuser + state: present + create_home: false + home: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir/randomuser" + register: create_user_no_create_home_with_no_parent + +- name: "Check if parent dir for home dir for user exists (after)" + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir" + register: create_user_no_create_home_with_no_parent_parent_dir_after + +- name: "Check if home for user is created" + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir/randomuser" + register: create_user_no_create_home_with_no_parent_home_dir + +- name: "Ensure user with non-existing parent paths with create_home: no was created successfully" + assert: + that: + - not create_user_no_create_home_with_no_parent_parent_dir_before.stat.exists + - not create_user_no_create_home_with_no_parent_parent_dir_after.stat.isdir is defined + - not create_user_no_create_home_with_no_parent_home_dir.stat.exists + +- name: Cleanup test account + user: + name: randomuser + state: absent + remove: yes diff --git a/test/integration/targets/user/tasks/test_create_user_password.yml b/test/integration/targets/user/tasks/test_create_user_password.yml new file mode 100644 index 00000000..02aae003 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user_password.yml @@ -0,0 +1,90 @@ +# test user add with password +- name: add an encrypted password for user + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + state: present + update_password: always + register: test_user_encrypt0 + +- name: there should not be warnings + assert: + that: "'warnings' not in test_user_encrypt0" + +# https://github.com/ansible/ansible/issues/65711 +- name: Test updating password only on creation + user: + name: ansibulluser + password: '*' + update_password: on_create + register: test_user_update_password + +- name: Ensure password was not changed + assert: + that: + - test_user_update_password is not changed + +- name: Verify password hash for Linux + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + block: + - name: LINUX | Get shadow entry for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure password hash was not removed + assert: + that: + - getent_shadow['ansibulluser'][1] != '*' + +- name: Test plaintext warning + when: ansible_facts.system != 'Darwin' + block: + - name: add an plaintext password for user + user: + name: ansibulluser + password: "plaintextpassword" + state: present + update_password: always + register: test_user_encrypt1 + + - name: there should be a warning complains that the password is plaintext + assert: + that: "'warnings' in test_user_encrypt1" + + - name: add an invalid hashed password + user: + name: ansibulluser + password: "$6$rounds=656000$tgK3gYTyRLUmhyv2$lAFrYUQwn7E6VsjPOwQwoSx30lmpiU9r/E0Al7tzKrR9mkodcMEZGe9OXD0H/clOn6qdsUnaL4zefy5fG+++++" + state: present + update_password: always + register: test_user_encrypt2 + + - name: there should be a warning complains about the character set of password + assert: + that: "'warnings' in test_user_encrypt2" + + - name: change password to '!' + user: + name: ansibulluser + password: '!' + register: test_user_encrypt3 + + - name: change password to '*' + user: + name: ansibulluser + password: '*' + register: test_user_encrypt4 + + - name: change password to '*************' + user: + name: ansibulluser + password: '*************' + register: test_user_encrypt5 + + - name: there should be no warnings when setting the password to '!', '*' or '*************' + assert: + that: + - "'warnings' not in test_user_encrypt3" + - "'warnings' not in test_user_encrypt4" + - "'warnings' not in test_user_encrypt5" diff --git a/test/integration/targets/user/tasks/test_create_user_uid.yml b/test/integration/targets/user/tasks/test_create_user_uid.yml new file mode 100644 index 00000000..9ac8a96f --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user_uid.yml @@ -0,0 +1,26 @@ +# test adding user with uid +# https://github.com/ansible/ansible/issues/62969 +- name: remove the test user + user: + name: ansibulluser + state: absent + +- name: try to create a user with uid + user: + name: ansibulluser + state: present + uid: 572 + register: user_test01_0 + +- name: create the user again + user: + name: ansibulluser + state: present + uid: 572 + register: user_test01_1 + +- name: validate results for testcase 0 + assert: + that: + - user_test01_0 is changed + - user_test01_1 is not changed diff --git a/test/integration/targets/user/tasks/test_expires.yml b/test/integration/targets/user/tasks/test_expires.yml new file mode 100644 index 00000000..8c238934 --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires.yml @@ -0,0 +1,147 @@ +# Date is March 3, 2050 +- name: Set user expiration + user: + name: ansibulluser + state: present + expires: 2529881062 + register: user_test_expires1 + tags: + - timezone + +- name: Set user expiration again to ensure no change is made + user: + name: ansibulluser + state: present + expires: 2529881062 + register: user_test_expires2 + tags: + - timezone + +- name: Ensure that account with expiration was created and did not change on subsequent run + assert: + that: + - user_test_expires1 is changed + - user_test_expires2 is not changed + +- name: Verify expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + that: + - getent_shadow['ansibulluser'][6] == '29281' + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + + +- name: Verify expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + that: + - bsd_account_expiration.stdout == '2529881062' + when: ansible_facts.os_family == 'FreeBSD' + +- name: Change timezone + timezone: + name: America/Denver + register: original_timezone + tags: + - timezone + +- name: Change system timezone to make sure expiration comparison works properly + block: + - name: Create user with expiration again to ensure no change is made in a new timezone + user: + name: ansibulluser + state: present + expires: 2529881062 + register: user_test_different_tz + tags: + - timezone + + - name: Ensure that no change was reported + assert: + that: + - user_test_different_tz is not changed + tags: + - timezone + + always: + - name: Restore original timezone - {{ original_timezone.diff.before.name }} + timezone: + name: "{{ original_timezone.diff.before.name }}" + when: original_timezone.diff.before.name != "n/a" + tags: + - timezone + + - name: Restore original timezone when n/a + file: + path: /etc/sysconfig/clock + state: absent + when: + - original_timezone.diff.before.name == "n/a" + - "'/etc/sysconfig/clock' in original_timezone.msg" + tags: + - timezone + + +- name: Unexpire user + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_expires3 + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for Linux/BSD + block: + - name: Unexpire user again to check for change + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_expires4 + + - name: Ensure first expiration reported a change and second did not + assert: + msg: The second run of the expiration removal task reported a change when it should not + that: + - user_test_expires3 is changed + - user_test_expires4 is not changed + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse', 'FreeBSD'] + +- name: Verify un expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" + that: + - bsd_account_expiration.stdout == '0' + when: ansible_facts.os_family == 'FreeBSD' diff --git a/test/integration/targets/user/tasks/test_expires_new_account.yml b/test/integration/targets/user/tasks/test_expires_new_account.yml new file mode 100644 index 00000000..b77d137f --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires_new_account.yml @@ -0,0 +1,55 @@ +# Test setting no expiration when creating a new account +# https://github.com/ansible/ansible/issues/44155 +- name: Remove ansibulluser + user: + name: ansibulluser + state: absent + +- name: Create user account without expiration + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_create_no_expires_1 + +- name: Create user account without expiration again + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_create_no_expires_2 + +- name: Ensure changes were made appropriately + assert: + msg: Setting 'expires='-1 resulted in incorrect changes + that: + - user_test_create_no_expires_1 is changed + - user_test_create_no_expires_2 is not changed + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" + that: + - bsd_account_expiration.stdout == '0' + when: ansible_facts.os_family == 'FreeBSD' diff --git a/test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml b/test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml new file mode 100644 index 00000000..77a07c4a --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml @@ -0,0 +1,112 @@ +# Test setting epoch 0 expiration when creating a new account, then removing the expiry +# https://github.com/ansible/ansible/issues/47114 +- name: Remove ansibulluser + user: + name: ansibulluser + state: absent + +- name: Create user account with epoch 0 expiration + user: + name: ansibulluser + state: present + expires: 0 + register: user_test_expires_create0_1 + +- name: Create user account with epoch 0 expiration again + user: + name: ansibulluser + state: present + expires: 0 + register: user_test_expires_create0_2 + +- name: Change the user account to remove the expiry time + user: + name: ansibulluser + expires: -1 + register: user_test_remove_expires_1 + +- name: Change the user account to remove the expiry time again + user: + name: ansibulluser + expires: -1 + register: user_test_remove_expires_2 + + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Ensure changes were made appropriately + assert: + msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes + that: + - user_test_expires_create0_1 is changed + - user_test_expires_create0_2 is not changed + - user_test_remove_expires_1 is changed + - user_test_remove_expires_2 is not changed + + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + + +- name: Verify proper expiration behavior for BSD + block: + - name: BSD | Ensure changes were made appropriately + assert: + msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes + that: + - user_test_expires_create0_1 is changed + - user_test_expires_create0_2 is not changed + - user_test_remove_expires_1 is not changed + - user_test_remove_expires_2 is not changed + when: ansible_facts.os_family == 'FreeBSD' + + +# Test expiration with a very large negative number. This should have the same +# result as setting -1. +- name: Set expiration date using very long negative number + user: + name: ansibulluser + state: present + expires: -2529881062 + register: user_test_expires5 + +- name: Ensure no change was made + assert: + that: + - user_test_expires5 is not changed + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" + that: + - bsd_account_expiration.stdout == '0' + when: ansible_facts.os_family == 'FreeBSD' diff --git a/test/integration/targets/user/tasks/test_local.yml b/test/integration/targets/user/tasks/test_local.yml new file mode 100644 index 00000000..16c79c57 --- /dev/null +++ b/test/integration/targets/user/tasks/test_local.yml @@ -0,0 +1,169 @@ +## Check local mode +# Even if we don't have a system that is bound to a directory, it's useful +# to run with local: true to exercise the code path that reads through the local +# user database file. +# https://github.com/ansible/ansible/issues/50947 + +- name: Create /etc/gshadow + file: + path: /etc/gshadow + state: touch + when: ansible_facts.os_family == 'Suse' + tags: + - user_test_local_mode + +- name: Create /etc/libuser.conf + file: + path: /etc/libuser.conf + state: touch + when: + - ansible_facts.distribution == 'Ubuntu' + - ansible_facts.distribution_major_version is version_compare('16', '==') + tags: + - user_test_local_mode + +- name: Ensure luseradd is present + action: "{{ ansible_facts.pkg_mgr }}" + args: + name: libuser + state: present + when: ansible_facts.system in ['Linux'] + tags: + - user_test_local_mode + +- name: Create local account that already exists to check for warning + user: + name: root + local: yes + register: local_existing + tags: + - user_test_local_mode + +- name: Create local_ansibulluser + user: + name: local_ansibulluser + state: present + local: yes + register: local_user_test_1 + tags: + - user_test_local_mode + +- name: Create local_ansibulluser again + user: + name: local_ansibulluser + state: present + local: yes + register: local_user_test_2 + tags: + - user_test_local_mode + +- name: Remove local_ansibulluser + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + register: local_user_test_remove_1 + tags: + - user_test_local_mode + +- name: Remove local_ansibulluser again + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + register: local_user_test_remove_2 + tags: + - user_test_local_mode + +- name: Create test groups + group: + name: "{{ item }}" + loop: + - testgroup1 + - testgroup2 + - testgroup3 + - testgroup4 + tags: + - user_test_local_mode + +- name: Create local_ansibulluser with groups + user: + name: local_ansibulluser + state: present + local: yes + groups: ['testgroup1', 'testgroup2'] + register: local_user_test_3 + ignore_errors: yes + tags: + - user_test_local_mode + +- name: Append groups for local_ansibulluser + user: + name: local_ansibulluser + state: present + local: yes + groups: ['testgroup3', 'testgroup4'] + append: yes + register: local_user_test_4 + ignore_errors: yes + tags: + - user_test_local_mode + +- name: Test append without groups for local_ansibulluser + user: + name: local_ansibulluser + state: present + append: yes + register: local_user_test_5 + ignore_errors: yes + tags: + - user_test_local_mode + +- name: Remove local_ansibulluser again + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + tags: + - user_test_local_mode + +- name: Remove test groups + group: + name: "{{ item }}" + state: absent + loop: + - testgroup1 + - testgroup2 + - testgroup3 + - testgroup4 + tags: + - user_test_local_mode + +- name: Ensure local user accounts were created and removed properly + assert: + that: + - local_user_test_1 is changed + - local_user_test_2 is not changed + - local_user_test_3 is changed + - local_user_test_4 is changed + - local_user_test_remove_1 is changed + - local_user_test_remove_2 is not changed + tags: + - user_test_local_mode + +- name: Ensure warnings were displayed properly + assert: + that: + - local_user_test_1['warnings'] | length > 0 + - local_user_test_1['warnings'] | first is search('The local user account may already exist') + - local_user_test_5['warnings'] is search("'append' is set, but no 'groups' are specified. Use 'groups'") + - local_existing['warnings'] is not defined + when: ansible_facts.system in ['Linux'] + tags: + - user_test_local_mode + +- name: Test expires for local users + import_tasks: test_local_expires.yml diff --git a/test/integration/targets/user/tasks/test_local_expires.yml b/test/integration/targets/user/tasks/test_local_expires.yml new file mode 100644 index 00000000..e6620353 --- /dev/null +++ b/test/integration/targets/user/tasks/test_local_expires.yml @@ -0,0 +1,333 @@ +--- +## local user expires +# Date is March 3, 2050 + +- name: Remove local_ansibulluser + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + tags: + - user_test_local_mode + +- name: Set user expiration + user: + name: local_ansibulluser + state: present + local: yes + expires: 2529881062 + register: user_test_local_expires1 + tags: + - timezone + - user_test_local_mode + +- name: Set user expiration again to ensure no change is made + user: + name: local_ansibulluser + state: present + local: yes + expires: 2529881062 + register: user_test_local_expires2 + tags: + - timezone + - user_test_local_mode + +- name: Ensure that account with expiration was created and did not change on subsequent run + assert: + that: + - user_test_local_expires1 is changed + - user_test_local_expires2 is not changed + tags: + - user_test_local_mode + +- name: Verify expiration date for Linux + block: + - name: LINUX | Get expiration date for local_ansibulluser + getent: + database: shadow + key: local_ansibulluser + tags: + - user_test_local_mode + + - name: LINUX | Ensure proper expiration date was set + assert: + that: + - getent_shadow['local_ansibulluser'][6] == '29281' + tags: + - user_test_local_mode + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Change timezone + timezone: + name: America/Denver + register: original_timezone + tags: + - timezone + - user_test_local_mode + +- name: Change system timezone to make sure expiration comparison works properly + block: + - name: Create user with expiration again to ensure no change is made in a new timezone + user: + name: local_ansibulluser + state: present + local: yes + expires: 2529881062 + register: user_test_local_different_tz + tags: + - timezone + - user_test_local_mode + + - name: Ensure that no change was reported + assert: + that: + - user_test_local_different_tz is not changed + tags: + - timezone + - user_test_local_mode + + always: + - name: Restore original timezone - {{ original_timezone.diff.before.name }} + timezone: + name: "{{ original_timezone.diff.before.name }}" + when: original_timezone.diff.before.name != "n/a" + tags: + - timezone + - user_test_local_mode + + - name: Restore original timezone when n/a + file: + path: /etc/sysconfig/clock + state: absent + when: + - original_timezone.diff.before.name == "n/a" + - "'/etc/sysconfig/clock' in original_timezone.msg" + tags: + - timezone + - user_test_local_mode + + +- name: Unexpire user + user: + name: local_ansibulluser + state: present + local: yes + expires: -1 + register: user_test_local_expires3 + tags: + - user_test_local_mode + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for local_ansibulluser + getent: + database: shadow + key: local_ansibulluser + tags: + - user_test_local_mode + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['local_ansibulluser'][6] }}" + that: + - not getent_shadow['local_ansibulluser'][6] or getent_shadow['local_ansibulluser'][6] | int < 0 + tags: + - user_test_local_mode + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for Linux/BSD + block: + - name: Unexpire user again to check for change + user: + name: local_ansibulluser + state: present + local: yes + expires: -1 + register: user_test_local_expires4 + tags: + - user_test_local_mode + + - name: Ensure first expiration reported a change and second did not + assert: + msg: The second run of the expiration removal task reported a change when it should not + that: + - user_test_local_expires3 is changed + - user_test_local_expires4 is not changed + tags: + - user_test_local_mode + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse', 'FreeBSD'] + +# Test setting no expiration when creating a new account +# https://github.com/ansible/ansible/issues/44155 +- name: Remove local_ansibulluser + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + tags: + - user_test_local_mode + +- name: Create user account without expiration + user: + name: local_ansibulluser + state: present + local: yes + expires: -1 + register: user_test_local_create_no_expires_1 + tags: + - user_test_local_mode + +- name: Create user account without expiration again + user: + name: local_ansibulluser + state: present + local: yes + expires: -1 + register: user_test_local_create_no_expires_2 + tags: + - user_test_local_mode + +- name: Ensure changes were made appropriately + assert: + msg: Setting 'expires='-1 resulted in incorrect changes + that: + - user_test_local_create_no_expires_1 is changed + - user_test_local_create_no_expires_2 is not changed + tags: + - user_test_local_mode + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for local_ansibulluser + getent: + database: shadow + key: local_ansibulluser + tags: + - user_test_local_mode + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['local_ansibulluser'][6] }}" + that: + - not getent_shadow['local_ansibulluser'][6] or getent_shadow['local_ansibulluser'][6] | int < 0 + tags: + - user_test_local_mode + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +# Test setting epoch 0 expiration when creating a new account, then removing the expiry +# https://github.com/ansible/ansible/issues/47114 +- name: Remove local_ansibulluser + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + tags: + - user_test_local_mode + +- name: Create user account with epoch 0 expiration + user: + name: local_ansibulluser + state: present + local: yes + expires: 0 + register: user_test_local_expires_create0_1 + tags: + - user_test_local_mode + +- name: Create user account with epoch 0 expiration again + user: + name: local_ansibulluser + state: present + local: yes + expires: 0 + register: user_test_local_expires_create0_2 + tags: + - user_test_local_mode + +- name: Change the user account to remove the expiry time + user: + name: local_ansibulluser + expires: -1 + local: yes + register: user_test_local_remove_expires_1 + tags: + - user_test_local_mode + +- name: Change the user account to remove the expiry time again + user: + name: local_ansibulluser + expires: -1 + local: yes + register: user_test_local_remove_expires_2 + tags: + - user_test_local_mode + + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Ensure changes were made appropriately + assert: + msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes + that: + - user_test_local_expires_create0_1 is changed + - user_test_local_expires_create0_2 is not changed + - user_test_local_remove_expires_1 is changed + - user_test_local_remove_expires_2 is not changed + tags: + - user_test_local_mode + + - name: LINUX | Get expiration date for local_ansibulluser + getent: + database: shadow + key: local_ansibulluser + tags: + - user_test_local_mode + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['local_ansibulluser'][6] }}" + that: + - not getent_shadow['local_ansibulluser'][6] or getent_shadow['local_ansibulluser'][6] | int < 0 + tags: + - user_test_local_mode + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +# Test expiration with a very large negative number. This should have the same +# result as setting -1. +- name: Set expiration date using very long negative number + user: + name: local_ansibulluser + state: present + local: yes + expires: -2529881062 + register: user_test_local_expires5 + tags: + - user_test_local_mode + +- name: Ensure no change was made + assert: + that: + - user_test_local_expires5 is not changed + tags: + - user_test_local_mode + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for local_ansibulluser + getent: + database: shadow + key: local_ansibulluser + tags: + - user_test_local_mode + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['local_ansibulluser'][6] }}" + that: + - not getent_shadow['local_ansibulluser'][6] or getent_shadow['local_ansibulluser'][6] | int < 0 + tags: + - user_test_local_mode + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] diff --git a/test/integration/targets/user/tasks/test_no_home_fallback.yml b/test/integration/targets/user/tasks/test_no_home_fallback.yml new file mode 100644 index 00000000..f7627fae --- /dev/null +++ b/test/integration/targets/user/tasks/test_no_home_fallback.yml @@ -0,0 +1,106 @@ +## create user without home and test fallback home dir create + +- name: Test home directory creation + when: ansible_facts.system != 'Darwin' + block: + - name: create the user + user: + name: ansibulluser + + - name: delete the user and home dir + user: + name: ansibulluser + state: absent + force: true + remove: true + + - name: create the user without home + user: + name: ansibulluser + create_home: no + + - name: create the user home dir + user: + name: ansibulluser + register: user_create_home_fallback + + - name: stat home dir + stat: + path: '{{ user_create_home_fallback.home }}' + register: user_create_home_fallback_dir + + - name: read UMASK from /etc/login.defs and return mode + shell: | + import re + import os + try: + for line in open('/etc/login.defs').readlines(): + m = re.match(r'^UMASK\s+(\d+)$', line) + if m: + umask = int(m.group(1), 8) + except: + umask = os.umask(0) + mode = oct(0o777 & ~umask) + print(str(mode).replace('o', '')) + args: + executable: "{{ ansible_python_interpreter }}" + register: user_login_defs_umask + + - name: validate that user home dir is created + assert: + that: + - user_create_home_fallback is changed + - user_create_home_fallback_dir.stat.exists + - user_create_home_fallback_dir.stat.isdir + - user_create_home_fallback_dir.stat.pw_name == 'ansibulluser' + - user_create_home_fallback_dir.stat.mode == user_login_defs_umask.stdout + +- name: Create non-system user + when: ansible_facts.distribution == "MacOSX" + block: + - name: create non-system user on macOS to test the shell is set to /bin/bash + user: + name: macosuser + register: macosuser_output + + - name: validate the shell is set to /bin/bash + assert: + that: + - 'macosuser_output.shell == "/bin/bash"' + + - name: cleanup + user: + name: macosuser + state: absent + + - name: create system user on macOS to test the shell is set to /usr/bin/false + user: + name: macosuser + system: yes + register: macosuser_output + + - name: validate the shell is set to /usr/bin/false + assert: + that: + - 'macosuser_output.shell == "/usr/bin/false"' + + - name: cleanup + user: + name: macosuser + state: absent + + - name: create non-system user on macos and set the shell to /bin/sh + user: + name: macosuser + shell: /bin/sh + register: macosuser_output + + - name: validate the shell is set to /bin/sh + assert: + that: + - 'macosuser_output.shell == "/bin/sh"' + + - name: cleanup + user: + name: macosuser + state: absent diff --git a/test/integration/targets/user/tasks/test_password_lock.yml b/test/integration/targets/user/tasks/test_password_lock.yml new file mode 100644 index 00000000..dde374ee --- /dev/null +++ b/test/integration/targets/user/tasks/test_password_lock.yml @@ -0,0 +1,140 @@ +- name: Test password lock + when: ansible_facts.system in ['FreeBSD', 'OpenBSD', 'Linux'] + block: + - name: Remove ansibulluser + user: + name: ansibulluser + state: absent + remove: yes + + - name: Create ansibulluser with password + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + + - name: Lock account without password parameter + user: + name: ansibulluser + password_lock: yes + register: password_lock_1 + + - name: Lock account without password parameter again + user: + name: ansibulluser + password_lock: yes + register: password_lock_2 + + - name: Unlock account without password parameter + user: + name: ansibulluser + password_lock: no + register: password_lock_3 + + - name: Unlock account without password parameter again + user: + name: ansibulluser + password_lock: no + register: password_lock_4 + + - name: Lock account with password parameter + user: + name: ansibulluser + password_lock: yes + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_5 + + - name: Lock account with password parameter again + user: + name: ansibulluser + password_lock: yes + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_6 + + - name: Unlock account with password parameter + user: + name: ansibulluser + password_lock: no + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_7 + + - name: Unlock account with password parameter again + user: + name: ansibulluser + password_lock: no + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_8 + + - name: Ensure task reported changes appropriately + assert: + msg: The password_lock tasks did not make changes appropriately + that: + - password_lock_1 is changed + - password_lock_2 is not changed + - password_lock_3 is changed + - password_lock_4 is not changed + - password_lock_5 is changed + - password_lock_6 is not changed + - password_lock_7 is changed + - password_lock_8 is not changed + + - name: Lock account + user: + name: ansibulluser + password_lock: yes + + - name: Verify account lock for BSD + when: ansible_facts.system in ['FreeBSD', 'OpenBSD'] + block: + - name: BSD | Get account status + shell: "{{ status_command[ansible_facts['system']] }}" + register: account_status_locked + + - name: Unlock account + user: + name: ansibulluser + password_lock: no + + - name: BSD | Get account status + shell: "{{ status_command[ansible_facts['system']] }}" + register: account_status_unlocked + + - name: FreeBSD | Ensure account is locked + assert: + that: + - "'LOCKED' in account_status_locked.stdout" + - "'LOCKED' not in account_status_unlocked.stdout" + when: ansible_facts['system'] == 'FreeBSD' + + - name: Verify account lock for Linux + when: ansible_facts.system == 'Linux' + block: + - name: LINUX | Get account status + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure account is locked + assert: + that: + - getent_shadow['ansibulluser'][0].startswith('!') + + - name: Unlock account + user: + name: ansibulluser + password_lock: no + + - name: LINUX | Get account status + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure account is unlocked + assert: + that: + - not getent_shadow['ansibulluser'][0].startswith('!') + + always: + - name: Unlock account + user: + name: ansibulluser + password_lock: no diff --git a/test/integration/targets/user/tasks/test_password_lock_new_user.yml b/test/integration/targets/user/tasks/test_password_lock_new_user.yml new file mode 100644 index 00000000..dd4f23da --- /dev/null +++ b/test/integration/targets/user/tasks/test_password_lock_new_user.yml @@ -0,0 +1,63 @@ +- name: Test password lock + when: ansible_facts.system in ['FreeBSD', 'OpenBSD', 'Linux'] + block: + - name: Remove ansibulluser + user: + name: ansibulluser + state: absent + remove: yes + + - name: Create ansibulluser with password and locked + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + password_lock: yes + register: create_with_lock_1 + + - name: Create ansibulluser with password and locked again + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + password_lock: yes + register: create_with_lock_2 + + - name: Ensure task reported changes appropriately + assert: + msg: The password_lock tasks did not make changes appropriately + that: + - create_with_lock_1 is changed + - create_with_lock_2 is not changed + + - name: Verify account lock for BSD + when: ansible_facts.system in ['FreeBSD', 'OpenBSD'] + block: + - name: BSD | Get account status + shell: "{{ status_command[ansible_facts['system']] }}" + register: account_status_locked + + - name: FreeBSD | Ensure account is locked + assert: + that: + - "'LOCKED' in account_status_locked.stdout" + when: ansible_facts.system == 'FreeBSD' + + + - name: Verify account lock for Linux + when: ansible_facts.system == 'Linux' + block: + - name: LINUX | Get account status + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure account is locked + assert: + that: + - getent_shadow['ansibulluser'][0].startswith('!') + + + always: + - name: Unlock account + user: + name: ansibulluser + password_lock: no diff --git a/test/integration/targets/user/tasks/test_remove_user.yml b/test/integration/targets/user/tasks/test_remove_user.yml new file mode 100644 index 00000000..dea71cbf --- /dev/null +++ b/test/integration/targets/user/tasks/test_remove_user.yml @@ -0,0 +1,19 @@ +- name: try to delete the user + user: + name: ansibulluser + state: absent + force: true + register: user_test2 + +- name: make a new list of users + script: userlist.sh {{ ansible_facts.distribution }} + register: user_names2 + +- debug: + var: user_names2 + verbosity: 2 + +- name: validate results for testcase 2 + assert: + that: + - '"ansibulluser" not in user_names2.stdout_lines' diff --git a/test/integration/targets/user/tasks/test_shadow_backup.yml b/test/integration/targets/user/tasks/test_shadow_backup.yml new file mode 100644 index 00000000..2655fbf2 --- /dev/null +++ b/test/integration/targets/user/tasks/test_shadow_backup.yml @@ -0,0 +1,21 @@ +- name: Test shadow backup on Solaris + when: ansible_facts.os_family == 'Solaris' + block: + - name: Create a user to test shadow file backup + user: + name: ansibulluser + state: present + register: result + + - name: Find shadow backup files + find: + path: /etc + patterns: 'shadow\..*~$' + use_regex: yes + register: shadow_backups + + - name: Assert that a backup file was created + assert: + that: + - result.bakup + - shadow_backups.files | map(attribute='path') | list | length > 0 diff --git a/test/integration/targets/user/tasks/test_ssh_key_passphrase.yml b/test/integration/targets/user/tasks/test_ssh_key_passphrase.yml new file mode 100644 index 00000000..bb0486da --- /dev/null +++ b/test/integration/targets/user/tasks/test_ssh_key_passphrase.yml @@ -0,0 +1,29 @@ +# Test creating ssh key with passphrase +- name: Remove ansibulluser + user: + name: ansibulluser + state: absent + +- name: Create user with ssh key + user: + name: ansibulluser + state: present + generate_ssh_key: yes + force: yes + ssh_key_file: "{{ output_dir }}/test_id_rsa" + ssh_key_passphrase: secret_passphrase + +- name: Unlock ssh key + command: "ssh-keygen -y -f {{ output_dir }}/test_id_rsa -P secret_passphrase" + register: result + +- name: Check that ssh key was unlocked successfully + assert: + that: + - result.rc == 0 + +- name: Clean ssh key + file: + path: "{{ output_dir }}/test_id_rsa" + state: absent + when: ansible_os_family == 'FreeBSD' diff --git a/test/integration/targets/user/vars/main.yml b/test/integration/targets/user/vars/main.yml new file mode 100644 index 00000000..4b328f71 --- /dev/null +++ b/test/integration/targets/user/vars/main.yml @@ -0,0 +1,13 @@ +user_home_prefix: + Linux: '/home' + FreeBSD: '/home' + SunOS: '/home' + Darwin: '/Users' + +status_command: + OpenBSD: "grep ansibulluser /etc/master.passwd | cut -d ':' -f 2" + FreeBSD: 'pw user show ansibulluser' + +default_user_group: + openSUSE Leap: users + MacOSX: admin |