1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

User module - Check local database when local is specified in the task (#51088)

The output of pw.getpwnam() does not distinbuish between local and remote accounts. It will return a result if an account exists locally or in the directory. When local is set to True in the task parameters, look through the local password database explicitly.

* Ensure luseradd is present for tests
* Add docs and warnings about local mode
This commit is contained in:
Sam Doran 2019-03-14 22:16:53 -04:00 committed by GitHub
parent 43a44e6f35
commit 1e595493d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 7 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- user - fix a bug when checking if a local user account exists on a system using directory authentication (https://github.com/ansible/ansible/issues/50947, https://github.com/ansible/ansible/issues/38206)

View file

@ -206,7 +206,9 @@ options:
- Forces the use of "local" command alternatives on platforms that implement it. - Forces the use of "local" command alternatives on platforms that implement it.
- This is useful in environments that use centralized authentification when you want to manipulate the local users - This is useful in environments that use centralized authentification when you want to manipulate the local users
(i.e. it uses C(luseradd) instead of C(useradd)). (i.e. it uses C(luseradd) instead of C(useradd)).
- This requires that these commands exist on the targeted host, otherwise it will be a fatal error. - This will check C(/etc/passwd) for an existing account before invoking commands. If the local account database
exists somewhere other than C(/etc/passwd), this setting will not work properly.
- This requires that the above commands as well as C(/etc/passwd) must exist on the target host, otherwise it will be a fatal error.
type: bool type: bool
default: no default: no
version_added: "2.4" version_added: "2.4"
@ -446,6 +448,7 @@ class User(object):
platform = 'Generic' platform = 'Generic'
distribution = None distribution = None
PASSWORDFILE = '/etc/passwd'
SHADOWFILE = '/etc/shadow' SHADOWFILE = '/etc/shadow'
SHADOWFILE_EXPIRE_INDEX = 7 SHADOWFILE_EXPIRE_INDEX = 7
LOGIN_DEFS = '/etc/login.defs' LOGIN_DEFS = '/etc/login.defs'
@ -840,11 +843,35 @@ class User(object):
return groups return groups
def user_exists(self): def user_exists(self):
try: # The pwd module does not distinguish between local and directory accounts.
if pwd.getpwnam(self.name): # It's output cannot be used to determine whether or not an account exists locally.
return True # It returns True if the account exists locally or in the directory, so instead
except KeyError: # look in the local PASSWORD file for an existing account.
return False if self.local:
if not os.path.exists(self.PASSWORDFILE):
self.module.fail_json(msg="'local: true' specified but unable to find local account file {0} to parse.".format(self.PASSWORDFILE))
exists = False
name_test = '{0}:'.format(self.name)
with open(self.PASSWORDFILE, 'rb') as f:
reversed_lines = f.readlines()[::-1]
for line in reversed_lines:
if line.startswith(to_bytes(name_test)):
exists = True
break
self.module.warn(
"'local: true' specified and user was not found in {file}. "
"The local user account may already exist if the local account database exists somewhere other than {file}.".format(file=self.PASSWORDFILE))
return exists
else:
try:
if pwd.getpwnam(self.name):
return True
except KeyError:
return False
def get_pwd_info(self): def get_pwd_info(self):
if not self.user_exists(): if not self.user_exists():

View file

@ -255,7 +255,7 @@
mode = oct(0o777 & ~umask) mode = oct(0o777 & ~umask)
print(str(mode).replace('o', '')) print(str(mode).replace('o', ''))
args: args:
executable: python executable: "{{ ansible_facts.python.executable }}"
register: user_login_defs_umask register: user_login_defs_umask
- name: validate that user home dir is created - name: validate that user home dir is created
@ -775,3 +775,85 @@
password_lock: no password_lock: no
when: ansible_facts['system'] in ['FreeBSD', 'OpenBSD', 'Linux'] when: ansible_facts['system'] in ['FreeBSD', 'OpenBSD', 'Linux']
## 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_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_3
tags:
- user_test_local_mode
- name: Remove local_ansibulluser again
user:
name: local_ansibulluser
state: absent
remove: yes
local: yes
register: local_user_test_4
tags:
- user_test_local_mode
- name: Ensure local user accounts were created
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 not changed
tags:
- user_test_local_mode