diff --git a/changelogs/fragments/fix_create_home.yml b/changelogs/fragments/fix_create_home.yml new file mode 100644 index 00000000000..46c774645a1 --- /dev/null +++ b/changelogs/fragments/fix_create_home.yml @@ -0,0 +1,4 @@ +bugfixes: + - >- + user - properly create home path on Linux when ``local: yes`` and parent directories + specified in ``home`` do not exist (https://github.com/ansible/ansible/pull/71952/) diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py index b57e6e13bcb..a7bfee46aa0 100644 --- a/lib/ansible/modules/user.py +++ b/lib/ansible/modules/user.py @@ -685,13 +685,8 @@ class User(object): cmd.append(self.comment) if self.home is not None: - # If the specified path to the user home contains parent directories that - # do not exist and create_home is True first create the parent directory - # since useradd cannot create it. if self.create_home: - parent = os.path.dirname(self.home) - if not os.path.isdir(parent): - self.create_homedir(self.home) + self.create_homedir(self.home) cmd.append('-d') cmd.append(self.home) @@ -713,19 +708,7 @@ class User(object): else: cmd.append(self.password) - if self.create_home: - if not self.local: - cmd.append('-m') - - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) - - if self.umask is not None: - cmd.append('-K') - cmd.append('UMASK=' + self.umask) - else: - cmd.append('-M') + cmd.append('-M') if self.system: cmd.append('-r') @@ -1250,18 +1233,26 @@ class User(object): os.makedirs(path) except OSError as e: self.module.exit_json(failed=True, msg="%s" % to_native(e)) - # get umask from /etc/login.defs and set correct home mode - if os.path.exists(self.LOGIN_DEFS): - with open(self.LOGIN_DEFS, 'r') as f: - for line in f: - m = re.match(r'^UMASK\s+(\d+)$', line) - if m: - umask = int(m.group(1), 8) - mode = 0o777 & ~umask - try: - os.chmod(path, mode) - except OSError as e: - self.module.exit_json(failed=True, msg="%s" % to_native(e)) + umask_string = None + # If an umask was set take it from there + if self.umask is not None: + umask_string = self.umask + else: + # try to get umask from /etc/login.defs + if os.path.exists(self.LOGIN_DEFS): + with open(self.LOGIN_DEFS, 'r') as f: + for line in f: + m = re.match(r'^UMASK\s+(\d+)$', line) + if m: + umask_string = m.group(1) + # set correct home mode if we have a umask + if umask_string is not None: + umask = int(umask_string, 8) + mode = 0o777 & ~umask + try: + os.chmod(path, mode) + except OSError as e: + self.module.exit_json(failed=True, msg="%s" % to_native(e)) def chown_homedir(self, uid, gid, path): try: diff --git a/test/integration/targets/user/tasks/test_local.yml b/test/integration/targets/user/tasks/test_local.yml index 16c79c57812..13d7ca30c8f 100644 --- a/test/integration/targets/user/tasks/test_local.yml +++ b/test/integration/targets/user/tasks/test_local.yml @@ -167,3 +167,6 @@ - name: Test expires for local users import_tasks: test_local_expires.yml + +- name: Test missing home directory parent directory for local users + import_tasks: test_local_missing_parent_dir.yml diff --git a/test/integration/targets/user/tasks/test_local_missing_parent_dir.yml b/test/integration/targets/user/tasks/test_local_missing_parent_dir.yml new file mode 100644 index 00000000000..fb992c09b3b --- /dev/null +++ b/test/integration/targets/user/tasks/test_local_missing_parent_dir.yml @@ -0,0 +1,56 @@ +--- +# Create a new local user account with a path that has parent directories that do not exist +- name: Create local user with home path that has parents that do not exist + user: + name: ansibulluser2 + state: present + local: yes + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_local_with_no_parent_1 + tags: + - user_test_local_mode + +- name: Create local user with home path that has parents that do not exist again + user: + name: ansibulluser2 + state: present + local: yes + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_local_with_no_parent_2 + tags: + - user_test_local_mode + +- name: Check the created home directory + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: home_local_with_no_parent_3 + tags: + - user_test_local_mode + +- name: Ensure user with non-existing parent paths was created successfully + assert: + that: + - create_home_local_with_no_parent_1 is changed + - create_home_local_with_no_parent_1.home == user_home_prefix[ansible_facts.system] ~ '/in2deep/ansibulluser2' + - create_home_local_with_no_parent_2 is not changed + - home_local_with_no_parent_3.stat.uid == create_home_local_with_no_parent_1.uid + - home_local_with_no_parent_3.stat.gr_name == default_local_user_group[ansible_facts.distribution] | default('ansibulluser2') + tags: + - user_test_local_mode + +- name: Cleanup test account + user: + name: ansibulluser2 + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + state: absent + local: yes + remove: yes + tags: + - user_test_local_mode + +- name: Remove testing dir + file: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/" + state: absent + tags: + - user_test_local_mode diff --git a/test/integration/targets/user/vars/main.yml b/test/integration/targets/user/vars/main.yml index 4b328f7184b..9fd09236363 100644 --- a/test/integration/targets/user/vars/main.yml +++ b/test/integration/targets/user/vars/main.yml @@ -11,3 +11,6 @@ status_command: default_user_group: openSUSE Leap: users MacOSX: admin + +default_local_user_group: + MacOSX: admin