summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/system/user.py
diff options
context:
space:
mode:
authorStrahinja Kustudic <kustodian@gmail.com>2019-01-14 22:01:26 +0100
committeransibot <ansibot@users.noreply.github.com>2019-01-14 16:01:26 -0500
commiteb8294e6d98b0ab7db587be3c8e99a51da6e245d (patch)
tree39091985ad5d26341ab2a15b6f0550ac50214dbb /lib/ansible/modules/system/user.py
parent37960ccc87fb3711893d986e6d512920e095044f (diff)
downloadansible-eb8294e6d98b0ab7db587be3c8e99a51da6e245d.tar.gz
Fix create home dir fallback (#49262)
When a user home dir is not created with `useradd`, the home dir will now be created with umask from /etc/login.defs. Also fixed a bug in which after a local user is deleted, and the same user exists in the central user management system, the module would create that user's home.
Diffstat (limited to 'lib/ansible/modules/system/user.py')
-rw-r--r--lib/ansible/modules/system/user.py171
1 files changed, 95 insertions, 76 deletions
diff --git a/lib/ansible/modules/system/user.py b/lib/ansible/modules/system/user.py
index d7d52e7a80..03a7ce5be6 100644
--- a/lib/ansible/modules/system/user.py
+++ b/lib/ansible/modules/system/user.py
@@ -421,6 +421,7 @@ class User(object):
distribution = None
SHADOWFILE = '/etc/shadow'
SHADOWFILE_EXPIRE_INDEX = 7
+ LOGIN_DEFS = '/etc/login.defs'
DATE_FORMAT = '%Y-%m-%d'
def __new__(cls, *args, **kwargs):
@@ -854,10 +855,11 @@ class User(object):
elif self.SHADOWFILE:
# Read shadow file for user's encrypted password string
if os.path.exists(self.SHADOWFILE) and os.access(self.SHADOWFILE, os.R_OK):
- for line in open(self.SHADOWFILE).readlines():
- if line.startswith('%s:' % self.name):
- passwd = line.split(':')[1]
- expires = line.split(':')[self.SHADOWFILE_EXPIRE_INDEX] or -1
+ with open(self.SHADOWFILE, 'r') as f:
+ for line in f:
+ if line.startswith('%s:' % self.name):
+ passwd = line.split(':')[1]
+ expires = line.split(':')[self.SHADOWFILE_EXPIRE_INDEX] or -1
return passwd, expires
def get_ssh_key_path(self):
@@ -970,9 +972,8 @@ class User(object):
def get_ssh_public_key(self):
ssh_public_key_file = '%s.pub' % self.get_ssh_key_path()
try:
- f = open(ssh_public_key_file)
- ssh_public_key = f.read().strip()
- f.close()
+ with open(ssh_public_key_file, 'r') as f:
+ ssh_public_key = f.read().strip()
except IOError:
return None
return ssh_public_key
@@ -1001,11 +1002,23 @@ class User(object):
shutil.copytree(skeleton, path, symlinks=True)
except OSError as e:
self.module.exit_json(failed=True, msg="%s" % to_native(e))
- else:
- try:
- os.makedirs(path)
- except OSError as e:
- self.module.exit_json(failed=True, msg="%s" % to_native(e))
+ else:
+ try:
+ 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))
def chown_homedir(self, uid, gid, path):
try:
@@ -1173,9 +1186,10 @@ class FreeBsdUser(User):
# find current login class
user_login_class = None
if os.path.exists(self.SHADOWFILE) and os.access(self.SHADOWFILE, os.R_OK):
- for line in open(self.SHADOWFILE).readlines():
- if line.startswith('%s:' % self.name):
- user_login_class = line.split(':')[4]
+ with open(self.SHADOWFILE, 'r') as f:
+ for line in f:
+ if line.startswith('%s:' % self.name):
+ user_login_class = line.split(':')[4]
# act only if login_class change
if self.login_class != user_login_class:
@@ -1632,20 +1646,21 @@ class SunOS(User):
minweeks = ''
maxweeks = ''
warnweeks = ''
- for line in open("/etc/default/passwd", 'r'):
- line = line.strip()
- if (line.startswith('#') or line == ''):
- continue
- m = re.match(r'^([^#]*)#(.*)$', line)
- if m: # The line contains a hash / comment
- line = m.group(1)
- key, value = line.split('=')
- if key == "MINWEEKS":
- minweeks = value.rstrip('\n')
- elif key == "MAXWEEKS":
- maxweeks = value.rstrip('\n')
- elif key == "WARNWEEKS":
- warnweeks = value.rstrip('\n')
+ with open("/etc/default/passwd", 'r') as f:
+ for line in f:
+ line = line.strip()
+ if (line.startswith('#') or line == ''):
+ continue
+ m = re.match(r'^([^#]*)#(.*)$', line)
+ if m: # The line contains a hash / comment
+ line = m.group(1)
+ key, value = line.split('=')
+ if key == "MINWEEKS":
+ minweeks = value.rstrip('\n')
+ elif key == "MAXWEEKS":
+ maxweeks = value.rstrip('\n')
+ elif key == "WARNWEEKS":
+ warnweeks = value.rstrip('\n')
except Exception as err:
self.module.fail_json(msg="failed to read /etc/default/passwd: %s" % to_native(err))
@@ -1724,35 +1739,37 @@ class SunOS(User):
minweeks, maxweeks, warnweeks = self.get_password_defaults()
try:
lines = []
- for line in open(self.SHADOWFILE, 'rb').readlines():
- line = to_native(line, errors='surrogate_or_strict')
- fields = line.strip().split(':')
- if not fields[0] == self.name:
- lines.append(line)
- continue
- fields[1] = self.password
- fields[2] = str(int(time.time() // 86400))
- if minweeks:
- try:
- fields[3] = str(int(minweeks) * 7)
- except ValueError:
- # mirror solaris, which allows for any value in this field, and ignores anything that is not an int.
- pass
- if maxweeks:
- try:
- fields[4] = str(int(maxweeks) * 7)
- except ValueError:
- # mirror solaris, which allows for any value in this field, and ignores anything that is not an int.
- pass
- if warnweeks:
- try:
- fields[5] = str(int(warnweeks) * 7)
- except ValueError:
- # mirror solaris, which allows for any value in this field, and ignores anything that is not an int.
- pass
- line = ':'.join(fields)
- lines.append('%s\n' % line)
- open(self.SHADOWFILE, 'w+').writelines(lines)
+ with open(self.SHADOWFILE, 'rb') as f:
+ for line in f:
+ line = to_native(line, errors='surrogate_or_strict')
+ fields = line.strip().split(':')
+ if not fields[0] == self.name:
+ lines.append(line)
+ continue
+ fields[1] = self.password
+ fields[2] = str(int(time.time() // 86400))
+ if minweeks:
+ try:
+ fields[3] = str(int(minweeks) * 7)
+ except ValueError:
+ # mirror solaris, which allows for any value in this field, and ignores anything that is not an int.
+ pass
+ if maxweeks:
+ try:
+ fields[4] = str(int(maxweeks) * 7)
+ except ValueError:
+ # mirror solaris, which allows for any value in this field, and ignores anything that is not an int.
+ pass
+ if warnweeks:
+ try:
+ fields[5] = str(int(warnweeks) * 7)
+ except ValueError:
+ # mirror solaris, which allows for any value in this field, and ignores anything that is not an int.
+ pass
+ line = ':'.join(fields)
+ lines.append('%s\n' % line)
+ with open(self.SHADOWFILE, 'w+') as f:
+ f.writelines(lines)
except Exception as err:
self.module.fail_json(msg="failed to update users password: %s" % to_native(err))
@@ -1843,23 +1860,25 @@ class SunOS(User):
minweeks, maxweeks, warnweeks = self.get_password_defaults()
try:
lines = []
- for line in open(self.SHADOWFILE, 'rb').readlines():
- line = to_native(line, errors='surrogate_or_strict')
- fields = line.strip().split(':')
- if not fields[0] == self.name:
- lines.append(line)
- continue
- fields[1] = self.password
- fields[2] = str(int(time.time() // 86400))
- if minweeks:
- fields[3] = str(int(minweeks) * 7)
- if maxweeks:
- fields[4] = str(int(maxweeks) * 7)
- if warnweeks:
- fields[5] = str(int(warnweeks) * 7)
- line = ':'.join(fields)
- lines.append('%s\n' % line)
- open(self.SHADOWFILE, 'w+').writelines(lines)
+ with open(self.SHADOWFILE, 'rb') as f:
+ for line in f:
+ line = to_native(line, errors='surrogate_or_strict')
+ fields = line.strip().split(':')
+ if not fields[0] == self.name:
+ lines.append(line)
+ continue
+ fields[1] = self.password
+ fields[2] = str(int(time.time() // 86400))
+ if minweeks:
+ fields[3] = str(int(minweeks) * 7)
+ if maxweeks:
+ fields[4] = str(int(maxweeks) * 7)
+ if warnweeks:
+ fields[5] = str(int(warnweeks) * 7)
+ line = ':'.join(fields)
+ lines.append('%s\n' % line)
+ with open(self.SHADOWFILE, 'w+'):
+ f.writelines(lines)
rc = 0
except Exception as err:
self.module.fail_json(msg="failed to update users password: %s" % to_native(err))
@@ -2638,7 +2657,7 @@ def main():
if err:
result['stderr'] = err
- if user.user_exists():
+ if user.user_exists() and user.state == 'present':
info = user.user_info()
if info is False:
result['msg'] = "failed to look up user name: %s" % user.name