Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
#!/usr/bin/python
|
2012-08-03 03:29:10 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
|
|
|
|
# (c) 2012, Stephen Fromm <sfromm@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/>.
|
|
|
|
|
2012-09-30 08:50:25 +02:00
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: user
|
|
|
|
author: Stephen Fromm
|
2012-10-01 09:18:54 +02:00
|
|
|
version_added: 0.2
|
2012-09-30 08:50:25 +02:00
|
|
|
short_description: Manage user accounts
|
|
|
|
requirements: [ useradd, userdel, usermod ]
|
|
|
|
description:
|
|
|
|
- Manage user accounts and user attributes.
|
|
|
|
options:
|
|
|
|
name:
|
|
|
|
required: true
|
2012-10-25 14:03:13 +02:00
|
|
|
aliases: [ "user" ]
|
2012-09-30 08:50:25 +02:00
|
|
|
description:
|
|
|
|
- Name of the user to create, remove or modify.
|
|
|
|
comment:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Optionally sets the description (aka I(GECOS)) of user account.
|
|
|
|
uid:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Optionally sets the I(UID) of the user.
|
|
|
|
group:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Optionally sets the user's primary group (takes a group name).
|
|
|
|
groups:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Puts the user in this comma-delimited list of groups.
|
|
|
|
append:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- If I(yes), will only add groups, not set them to just the list
|
|
|
|
in I(groups).
|
|
|
|
shell:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Optionally set the user's shell.
|
|
|
|
home:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Optionally set the user's home directory.
|
|
|
|
password:
|
|
|
|
required: false
|
|
|
|
description:
|
|
|
|
- Optionally set the user's password to this crypted value. See
|
|
|
|
the user example in the github examples directory for what this looks
|
|
|
|
like in a playbook.
|
|
|
|
state:
|
|
|
|
required: false
|
|
|
|
default: "present"
|
|
|
|
choices: [ present, absent ]
|
|
|
|
description:
|
|
|
|
- Whether the account should exist. When I(absent), removes
|
|
|
|
the user account.
|
|
|
|
createhome:
|
|
|
|
required: false
|
|
|
|
default: "yes"
|
|
|
|
choices: [ yes, no ]
|
|
|
|
description:
|
|
|
|
- Unless set to I(no), a home directory will be made for the user
|
|
|
|
when the account is created.
|
|
|
|
system:
|
|
|
|
required: false
|
|
|
|
default: "no"
|
|
|
|
choices: [ yes, no ]
|
|
|
|
description:
|
|
|
|
- When creating an account, setting this to I(yes) makes the user a
|
|
|
|
system account. This setting cannot be changed on existing users.
|
|
|
|
force:
|
|
|
|
required: false
|
|
|
|
default: "no"
|
|
|
|
choices: [ yes, no ]
|
|
|
|
description:
|
|
|
|
- When used with I(state=absent), behavior is as with
|
|
|
|
I(userdel --force).
|
|
|
|
remove:
|
|
|
|
required: false
|
|
|
|
default: "no"
|
|
|
|
choices: [ yes, no ]
|
|
|
|
description:
|
|
|
|
- When used with I(state=absent), behavior is as with
|
|
|
|
I(userdel --remove).
|
2012-10-20 07:00:31 +02:00
|
|
|
ssh_key:
|
|
|
|
required: false
|
|
|
|
choices: [ generate ]
|
2012-10-24 07:35:56 +02:00
|
|
|
version_added: "0.9"
|
2012-10-20 07:00:31 +02:00
|
|
|
description:
|
|
|
|
- Whether to generate a SSH key for the user in question.
|
|
|
|
This will B(not) overwrite an existing SSH key.
|
|
|
|
ssh_key_bits:
|
|
|
|
required: false
|
|
|
|
default: 2048
|
2012-10-24 07:35:56 +02:00
|
|
|
version_added: "0.9"
|
2012-10-20 07:00:31 +02:00
|
|
|
description:
|
|
|
|
- Optionally specify number of bits in SSH key to create.
|
|
|
|
ssh_key_type:
|
|
|
|
required: false
|
|
|
|
default: rsa
|
2012-10-24 07:35:56 +02:00
|
|
|
version_added: "0.9"
|
2012-10-20 07:00:31 +02:00
|
|
|
description:
|
2012-10-25 20:55:09 +02:00
|
|
|
- Optionally specify the type of SSH key to generate.
|
2012-10-20 07:00:31 +02:00
|
|
|
Available SSH key types will depend on implementation
|
2012-10-24 07:35:56 +02:00
|
|
|
present on target host.
|
2012-10-20 07:00:31 +02:00
|
|
|
ssh_key_file:
|
|
|
|
required: false
|
|
|
|
default: $HOME/.ssh/id_rsa
|
2012-10-24 07:35:56 +02:00
|
|
|
version_added: "0.9"
|
2012-10-20 07:00:31 +02:00
|
|
|
description:
|
2012-10-24 07:35:56 +02:00
|
|
|
- Optionally specify the SSH key filename.
|
2012-10-20 07:00:31 +02:00
|
|
|
ssh_key_comment:
|
|
|
|
required: false
|
|
|
|
default: ansible-generated
|
2012-10-24 07:35:56 +02:00
|
|
|
version_added: "0.9"
|
2012-10-20 07:00:31 +02:00
|
|
|
description:
|
|
|
|
- Optionally define the comment for the SSH key.
|
|
|
|
ssh_key_passphrase:
|
|
|
|
required: false
|
2012-10-24 07:35:56 +02:00
|
|
|
version_added: "0.9"
|
2012-10-20 07:00:31 +02:00
|
|
|
description:
|
|
|
|
- Set a passphrase for the SSH key. If no
|
|
|
|
passphrase is provided, the SSH key will default to
|
2012-10-24 07:35:56 +02:00
|
|
|
having no passphrase.
|
2012-10-18 03:55:58 +02:00
|
|
|
examples:
|
2012-10-23 15:14:01 +02:00
|
|
|
- code: 'user: name=johnd comment="John Doe" uid=1040'
|
2012-10-18 03:55:58 +02:00
|
|
|
description: "Add the user 'johnd' with a specific uid and a primary group of 'admin'"
|
2012-10-23 15:14:01 +02:00
|
|
|
- code: "user: name=johnd state=absent remove=yes"
|
2012-10-18 03:55:58 +02:00
|
|
|
description: "Remove the user 'johnd'"
|
2012-10-23 15:14:01 +02:00
|
|
|
- code: 'user: name=jsmith ssh_key=generate ssh_key_bits=2048'
|
2012-10-20 07:00:31 +02:00
|
|
|
description: "Create a 2048-bit SSH key for user jsmith"
|
2012-09-30 08:50:25 +02:00
|
|
|
'''
|
|
|
|
|
2012-08-08 08:57:17 +02:00
|
|
|
import os
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
import pwd
|
2012-03-27 22:43:36 +02:00
|
|
|
import grp
|
2012-10-29 23:00:58 +01:00
|
|
|
import syslog
|
|
|
|
import platform
|
2012-05-08 19:40:44 +02:00
|
|
|
try:
|
|
|
|
import spwd
|
|
|
|
HAVE_SPWD=True
|
|
|
|
except:
|
|
|
|
HAVE_SPWD=False
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
|
|
|
|
class User(object):
|
2012-11-01 20:16:54 +01:00
|
|
|
"""
|
|
|
|
This is a generic User manipulation class - generic in this case
|
|
|
|
meaning it is aimed at Linuxish platforms using useradd/userdel/usermod
|
|
|
|
On object creation it will load the most appropriate subclass based
|
|
|
|
on the platform and distribution.
|
|
|
|
|
|
|
|
A subclass may wish to override the following action methods:-
|
|
|
|
- create_user()
|
|
|
|
- remove_user()
|
|
|
|
- modify_user()
|
|
|
|
- ssh_key_gen()
|
|
|
|
- ssh_key_fingerprint()
|
|
|
|
|
|
|
|
The main function also uses the following User methods to decide
|
|
|
|
what to do:-
|
|
|
|
- user_exists()
|
|
|
|
|
|
|
|
All subclasses MUST define platform and distribution (which may be None).
|
|
|
|
"""
|
2012-10-29 23:00:58 +01:00
|
|
|
|
|
|
|
platform = 'Generic'
|
|
|
|
distribution = None
|
|
|
|
SHADOWFILE = '/etc/shadow'
|
|
|
|
|
|
|
|
def __new__(cls, *arguments, **keyword):
|
|
|
|
subclass = None
|
|
|
|
this_platform = platform.system()
|
|
|
|
if this_platform == 'Linux':
|
|
|
|
try:
|
|
|
|
distribution = platform.linux_distribution()[0].capitalize
|
|
|
|
except:
|
|
|
|
distribution = platform.dist()[0].capitalize
|
|
|
|
else:
|
|
|
|
distribution = None
|
|
|
|
|
|
|
|
# get the most specific superclass for this platform
|
|
|
|
if distribution is not None:
|
|
|
|
for sc in User.__subclasses__():
|
|
|
|
if sc.distribution is not None and sc.distribution == distribution and sc.platform == this_platform:
|
|
|
|
subclass = sc
|
|
|
|
if subclass is None:
|
|
|
|
for sc in User.__subclasses__():
|
|
|
|
if sc.platform == this_platform and sc.distribution is None:
|
|
|
|
subclass = sc
|
|
|
|
if subclass is None:
|
|
|
|
subclass = cls
|
|
|
|
|
|
|
|
return super(cls, subclass).__new__(subclass, *arguments, **keyword)
|
|
|
|
|
|
|
|
def __init__(self, module):
|
|
|
|
self.module = module
|
|
|
|
self.state = module.params['state']
|
|
|
|
self.name = module.params['name']
|
|
|
|
self.uid = module.params['uid']
|
|
|
|
self.group = module.params['group']
|
|
|
|
self.groups = module.params['groups']
|
|
|
|
self.comment = module.params['comment']
|
|
|
|
self.home = module.params['home']
|
|
|
|
self.shell = module.params['shell']
|
|
|
|
self.password = module.params['password']
|
|
|
|
self.force = module.boolean(module.params['force'])
|
|
|
|
self.remove = module.boolean(module.params['remove'])
|
|
|
|
self.createhome = module.boolean(module.params['createhome'])
|
|
|
|
self.system = module.boolean(module.params['system'])
|
|
|
|
self.append = module.boolean(module.params['append'])
|
|
|
|
self.sshkeygen = module.boolean(module.params['ssh_key'])
|
|
|
|
self.ssh_bits = module.params['ssh_key_bits']
|
|
|
|
self.ssh_type = module.params['ssh_key_type']
|
|
|
|
self.ssh_comment = module.params['ssh_key_comment']
|
|
|
|
self.ssh_passphrase = module.params['ssh_key_passphrase']
|
|
|
|
if module.params['ssh_key_file'] is not None:
|
|
|
|
self.ssh_file = module.params['ssh_key_file']
|
|
|
|
else:
|
|
|
|
self.ssh_file = os.path.join('.ssh', 'id_%s' % self.ssh_type)
|
2012-10-29 23:04:06 +01:00
|
|
|
|
|
|
|
# select whether we dump additional debug info through syslog
|
|
|
|
self.syslogging = False
|
2012-10-29 23:00:58 +01:00
|
|
|
|
|
|
|
def execute_command(self,cmd):
|
|
|
|
if self.syslogging:
|
|
|
|
syslog.openlog('ansible-%s' % os.path.basename(__file__))
|
|
|
|
syslog.syslog(syslog.LOG_NOTICE, 'Command %s' % '|'.join(cmd))
|
|
|
|
|
|
|
|
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
(out, err) = p.communicate()
|
|
|
|
rc = p.returncode
|
|
|
|
return (rc, out, err)
|
|
|
|
|
|
|
|
def remove_user_userdel(self):
|
|
|
|
cmd = [self.module.get_bin_path('userdel', True)]
|
|
|
|
if self.force:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-f')
|
2012-10-29 23:00:58 +01:00
|
|
|
elif self.remove:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-r')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.name)
|
|
|
|
|
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
def create_user_useradd(self, command_name='useradd'):
|
|
|
|
cmd = [self.module.get_bin_path(command_name, True)]
|
|
|
|
|
|
|
|
if self.uid is not None:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-u')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.uid)
|
|
|
|
|
|
|
|
if self.group is not None:
|
|
|
|
if not user.group_exists(self.group):
|
|
|
|
self.module.fail_json(msg="Group %s does not exist" % self.group)
|
2012-03-28 23:12:35 +02:00
|
|
|
cmd.append('-g')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.group)
|
|
|
|
|
|
|
|
if self.groups is not None:
|
|
|
|
for g in self.groups.split(','):
|
|
|
|
if not self.group_exists(g):
|
|
|
|
self.module.fail_json(msg="Group %s does not exist" % (g))
|
2012-03-28 23:12:35 +02:00
|
|
|
cmd.append('-G')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.groups)
|
|
|
|
|
|
|
|
if self.comment is not None:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-c')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.comment)
|
|
|
|
|
|
|
|
if self.home is not None:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-d')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.home)
|
|
|
|
|
|
|
|
if self.shell is not None:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-s')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.shell)
|
|
|
|
|
|
|
|
if self.password is not None:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-p')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.password)
|
|
|
|
|
|
|
|
if self.createhome:
|
|
|
|
cmd.append('-m')
|
|
|
|
else:
|
|
|
|
cmd.append('-M')
|
|
|
|
|
|
|
|
if self.system:
|
2012-05-01 21:38:55 +02:00
|
|
|
cmd.append('-r')
|
2012-10-29 23:00:58 +01:00
|
|
|
|
|
|
|
cmd.append(self.name)
|
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
|
|
|
|
def modify_user_usermod(self):
|
|
|
|
cmd = [self.module.get_bin_path('usermod', True)]
|
|
|
|
info = self.user_info()
|
|
|
|
|
|
|
|
if self.uid is not None and info[2] != int(self.uid):
|
|
|
|
cmd.append('-u')
|
|
|
|
cmd.append(self.uid)
|
|
|
|
|
|
|
|
if self.group is not None:
|
|
|
|
if not self.group_exists(self.group):
|
|
|
|
module.fail_json(msg="Group %s does not exist" % self.group)
|
|
|
|
ginfo = self.group_info(self.group)
|
2012-03-28 23:12:35 +02:00
|
|
|
if info[3] != ginfo[2]:
|
|
|
|
cmd.append('-g')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.group)
|
|
|
|
|
|
|
|
if self.groups is not None:
|
|
|
|
current_groups = self.user_group_membership()
|
|
|
|
groups = self.groups.split(',')
|
2012-05-08 01:43:51 +02:00
|
|
|
for g in groups:
|
2012-10-29 23:00:58 +01:00
|
|
|
if not self.group_exists(g):
|
2012-07-23 07:56:48 +02:00
|
|
|
module.fail_json(msg="Group %s does not exist" % (g))
|
2012-10-29 23:00:58 +01:00
|
|
|
|
2012-05-08 01:43:51 +02:00
|
|
|
group_diff = set(sorted(current_groups)).symmetric_difference(set(sorted(groups)))
|
|
|
|
groups_need_mod = False
|
|
|
|
|
|
|
|
if group_diff:
|
2012-10-29 23:00:58 +01:00
|
|
|
if self.append:
|
2012-05-08 01:43:51 +02:00
|
|
|
for g in groups:
|
|
|
|
if g in group_diff:
|
|
|
|
cmd.append('-a')
|
|
|
|
groups_need_mod = True
|
2012-10-29 23:00:58 +01:00
|
|
|
break
|
2012-05-08 01:43:51 +02:00
|
|
|
else:
|
2012-08-11 18:35:58 +02:00
|
|
|
groups_need_mod = True
|
2012-05-08 01:43:51 +02:00
|
|
|
|
|
|
|
if groups_need_mod:
|
2012-03-28 23:12:35 +02:00
|
|
|
cmd.append('-G')
|
2012-05-08 01:43:51 +02:00
|
|
|
cmd.append(','.join(groups))
|
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
if self.comment is not None and info[4] != self.comment:
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
cmd.append('-c')
|
2012-10-29 23:00:58 +01:00
|
|
|
cmd.append(self.comment)
|
|
|
|
|
|
|
|
if self.home is not None and info[5] != self.home:
|
|
|
|
cmd.append('-d')
|
|
|
|
cmd.append(self.home)
|
|
|
|
|
|
|
|
if self.shell is not None and info[6] != self.shell:
|
|
|
|
cmd.append('-s')
|
|
|
|
cmd.append(self.shell)
|
|
|
|
|
|
|
|
if self.password is not None and info[1] != self.password:
|
|
|
|
cmd.append('-p')
|
|
|
|
cmd.append(self.password)
|
|
|
|
|
|
|
|
# skip if no changes to be made
|
|
|
|
if len(cmd) == 1:
|
|
|
|
return (None, '', '')
|
|
|
|
|
|
|
|
cmd.append(self.name)
|
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
def group_exists(self,group):
|
|
|
|
try:
|
|
|
|
if group.isdigit():
|
|
|
|
if grp.getgrgid(group):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
if grp.getgrnam(group):
|
|
|
|
return True
|
|
|
|
except KeyError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def group_info(self,group):
|
|
|
|
if not self.group_exists(group):
|
|
|
|
return False
|
2012-03-27 22:43:36 +02:00
|
|
|
if group.isdigit():
|
2012-10-29 23:00:58 +01:00
|
|
|
return list(grp.getgrgid(group))
|
2012-03-27 22:43:36 +02:00
|
|
|
else:
|
2012-10-29 23:00:58 +01:00
|
|
|
return list(grp.getgrnam(group))
|
|
|
|
|
|
|
|
def user_group_membership(self):
|
|
|
|
groups = []
|
|
|
|
info = self.get_pwd_info()
|
|
|
|
for group in grp.getgrall():
|
|
|
|
if self.name in group[3] and info[3] != group[2]:
|
|
|
|
groups.append(group[0])
|
|
|
|
return groups
|
|
|
|
|
|
|
|
def user_exists(self):
|
2012-08-08 08:57:17 +02:00
|
|
|
try:
|
2012-10-29 23:00:58 +01:00
|
|
|
if pwd.getpwnam(self.name):
|
|
|
|
return True
|
2012-08-08 08:57:17 +02:00
|
|
|
except KeyError:
|
2012-10-29 23:00:58 +01:00
|
|
|
return False
|
|
|
|
|
|
|
|
def get_pwd_info(self):
|
|
|
|
if not self.user_exists():
|
|
|
|
return False
|
|
|
|
return list(pwd.getpwnam(self.name))
|
|
|
|
|
|
|
|
def user_info(self):
|
|
|
|
if not self.user_exists():
|
|
|
|
return False
|
|
|
|
info = self.get_pwd_info()
|
|
|
|
if len(info[1]) == 1 or len(info[1]) == 0:
|
|
|
|
info[1] = self.user_password()
|
|
|
|
return info
|
|
|
|
|
|
|
|
def user_password(self):
|
|
|
|
passwd = ''
|
|
|
|
if not self.user_exists():
|
2012-08-08 08:57:17 +02:00
|
|
|
return passwd
|
2012-10-29 23:00:58 +01:00
|
|
|
if HAVE_SPWD:
|
|
|
|
try:
|
|
|
|
passwd = spwd.getspnam(self.name)[1]
|
|
|
|
except KeyError:
|
|
|
|
return passwd
|
|
|
|
else:
|
|
|
|
# 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]
|
|
|
|
return passwd
|
|
|
|
|
|
|
|
def get_ssh_key_path(self):
|
|
|
|
info = self.user_info
|
|
|
|
if os.path.isabs(self.ssh_file):
|
|
|
|
ssh_key_file = self.ssh_file
|
|
|
|
else:
|
|
|
|
ssh_key_file = os.path.join(info[5], self.ssh_file)
|
|
|
|
return ssh_key_file
|
|
|
|
|
|
|
|
def ssh_key_gen(self):
|
|
|
|
info = self.user_info
|
|
|
|
if not os.path.exists(info[5]):
|
|
|
|
return (1, '', 'User %s home directory does not exist' % self.name)
|
|
|
|
ssh_key_file = self.get_ssh_key_path
|
|
|
|
ssh_dir = os.path.dirname(ssh_key_file)
|
|
|
|
if not os.path.exists(ssh_dir):
|
|
|
|
try:
|
|
|
|
os.mkdir(ssh_dir, 0700)
|
|
|
|
except OSError, e:
|
|
|
|
return (1, '', 'Failed to create %s: %s' % (ssh_dir, str(e)))
|
|
|
|
if os.path.exists(ssh_key_file):
|
|
|
|
return (None, 'Key already exists', '')
|
|
|
|
cmd = [self.module.get_bin_path('ssh-keygen', True)]
|
|
|
|
cmd.append('-t')
|
|
|
|
cmd.append(self.ssh_type)
|
|
|
|
cmd.append('-b')
|
|
|
|
cmd.append(self.ssh_bits)
|
|
|
|
cmd.append('-C')
|
|
|
|
cmd.append(self.ssh_comment)
|
|
|
|
cmd.append('-f')
|
|
|
|
cmd.append(ssh_key_file)
|
|
|
|
cmd.append('-N')
|
|
|
|
if self.ssh_passphrase is not None:
|
|
|
|
cmd.append(self.ssh_passphrase)
|
|
|
|
else:
|
|
|
|
cmd.append('')
|
2012-10-20 07:00:31 +02:00
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
return self.execute_command(cmd)
|
2012-10-20 07:00:31 +02:00
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
def ssh_key_fingerprint(self):
|
|
|
|
ssh_key_file = self.get_ssh_key_path
|
|
|
|
if not os.path.exists(ssh_key_file):
|
|
|
|
return (1, 'SSH Key file %s does not exist' % ssh_key_file, '')
|
|
|
|
cmd = [module.get_bin_path('ssh-keygen', True)]
|
|
|
|
cmd.append('-l')
|
|
|
|
cmd.append('-f')
|
|
|
|
cmd.append(ssh_key_file)
|
2012-10-20 07:00:31 +02:00
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
def create_user(self):
|
|
|
|
# by default we use the create_user_useradd method
|
|
|
|
return self.create_user_useradd()
|
|
|
|
|
|
|
|
def remove_user(self):
|
|
|
|
# by default we use the remove_user_userdel method
|
|
|
|
return self.remove_user_userdel()
|
|
|
|
|
|
|
|
def modify_user(self):
|
|
|
|
# by default we use the modify_user_usermod method
|
|
|
|
return self.modify_user_usermod()
|
|
|
|
|
|
|
|
|
|
|
|
# ===========================================
|
|
|
|
|
|
|
|
class FreeBSD_User(User):
|
2012-11-01 20:16:54 +01:00
|
|
|
"""
|
|
|
|
This is a FreeBSD User manipulation class - it uses the pw command
|
|
|
|
to manipulate the user database, followed by the chpass command
|
|
|
|
to change the password.
|
|
|
|
|
|
|
|
This overrides the following methods from the generic class:-
|
|
|
|
- create_user()
|
|
|
|
- remove_user()
|
|
|
|
- modify_user()
|
|
|
|
"""
|
2012-10-29 23:00:58 +01:00
|
|
|
|
|
|
|
platform = 'FreeBSD'
|
|
|
|
distribution = None
|
|
|
|
SHADOWFILE = '/etc/master.passwd'
|
|
|
|
|
|
|
|
def remove_user(self):
|
|
|
|
cmd = [self.module.get_bin_path('pw', True),
|
|
|
|
'userdel',
|
|
|
|
'-n',
|
|
|
|
self.name ]
|
|
|
|
if self.remove:
|
|
|
|
cmd.append('-r')
|
|
|
|
|
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
def create_user(self):
|
|
|
|
cmd = [self.module.get_bin_path('pw', True),
|
|
|
|
'useradd',
|
|
|
|
'-n',
|
|
|
|
self.name ]
|
|
|
|
|
|
|
|
if self.uid is not None:
|
|
|
|
cmd.append('-u')
|
|
|
|
cmd.append(self.uid)
|
|
|
|
|
|
|
|
if self.comment is not None:
|
|
|
|
cmd.append('-c')
|
|
|
|
cmd.append(self.comment)
|
|
|
|
|
|
|
|
if self.home is not None:
|
|
|
|
cmd.append('-d')
|
|
|
|
cmd.append(self.home)
|
|
|
|
|
|
|
|
if self.group is not None:
|
|
|
|
if not user.group_exists(self.group):
|
|
|
|
self.module.fail_json(msg="Group %s does not exist" % self.group)
|
|
|
|
cmd.append('-g')
|
|
|
|
cmd.append(self.group)
|
|
|
|
|
|
|
|
if self.groups is not None:
|
|
|
|
for g in self.groups.split(','):
|
|
|
|
if not self.group_exists(g):
|
|
|
|
self.module.fail_json(msg="Group %s does not exist" % (g))
|
|
|
|
cmd.append('-G')
|
|
|
|
cmd.append(self.groups)
|
|
|
|
|
|
|
|
if self.createhome:
|
|
|
|
cmd.append('-m')
|
|
|
|
|
|
|
|
if self.shell is not None:
|
|
|
|
cmd.append('-s')
|
|
|
|
cmd.append(self.shell)
|
|
|
|
|
|
|
|
# system cannot be handled currently - should we error if its requested?
|
|
|
|
# create the user
|
|
|
|
(rc, out, err) = self.execute_command(cmd)
|
|
|
|
if rc is not None and rc != 0:
|
|
|
|
module.fail_json(name=self.name, msg=err, rc=rc)
|
|
|
|
|
|
|
|
# we have to set the password in a second command
|
|
|
|
if self.password is not None:
|
|
|
|
cmd = [self.module.get_bin_path('chpass', True),
|
|
|
|
'-p',
|
|
|
|
self.password,
|
|
|
|
self.name ]
|
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
return (rc, out, err)
|
|
|
|
|
|
|
|
def modify_user(self):
|
|
|
|
cmd = [self.module.get_bin_path('pw', True),
|
|
|
|
'usermod',
|
|
|
|
'-n',
|
|
|
|
self.name ]
|
|
|
|
info = self.user_info()
|
|
|
|
|
|
|
|
if self.uid is not None and info[2] != int(self.uid):
|
|
|
|
cmd.append('-u')
|
|
|
|
cmd.append(self.uid)
|
|
|
|
|
|
|
|
if self.comment is not None and info[4] != self.comment:
|
|
|
|
cmd.append('-c')
|
|
|
|
cmd.append(self.comment)
|
|
|
|
|
|
|
|
if self.home is not None and info[5] != self.home:
|
|
|
|
cmd.append('-d')
|
|
|
|
cmd.append(self.home)
|
|
|
|
|
|
|
|
if self.group is not None:
|
|
|
|
if not user.group_exists(self.group):
|
|
|
|
self.module.fail_json(msg="Group %s does not exist" % self.group)
|
|
|
|
ginfo = self.group_info(self.group)
|
|
|
|
if info[3] != ginfo[2]:
|
|
|
|
cmd.append('-g')
|
|
|
|
cmd.append(self.group)
|
|
|
|
|
|
|
|
if self.shell is not None and info[6] != self.shell:
|
|
|
|
cmd.append('-s')
|
|
|
|
cmd.append(self.shell)
|
|
|
|
|
|
|
|
if self.groups is not None:
|
|
|
|
current_groups = self.user_group_membership()
|
|
|
|
groups = self.groups.split(',')
|
|
|
|
for g in groups:
|
|
|
|
if not self.group_exists(g):
|
|
|
|
module.fail_json(msg="Group %s does not exist" % (g))
|
|
|
|
|
|
|
|
group_diff = set(sorted(current_groups)).symmetric_difference(set(sorted(groups)))
|
|
|
|
groups_need_mod = False
|
|
|
|
|
|
|
|
if group_diff:
|
|
|
|
if self.append:
|
|
|
|
for g in groups:
|
|
|
|
if g in group_diff:
|
|
|
|
groups_need_mod = True
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
groups_need_mod = True
|
|
|
|
|
|
|
|
if groups_need_mod:
|
|
|
|
new_groups = groups
|
|
|
|
if self.append:
|
|
|
|
new_groups.append(current_groups)
|
|
|
|
cmd.append(','.join(new_groups))
|
|
|
|
|
|
|
|
# modify the user
|
|
|
|
(rc, out, err) = self.execute_command(cmd)
|
|
|
|
if rc is not None and rc != 0:
|
|
|
|
module.fail_json(name=self.name, msg=err, rc=rc)
|
|
|
|
|
|
|
|
# we have to set the password in a second command
|
|
|
|
if self.password is not None and info[1] != self.password:
|
|
|
|
cmd = [self.module.get_bin_path('chpass', True),
|
|
|
|
'-p',
|
|
|
|
self.password,
|
|
|
|
self.name ]
|
|
|
|
return self.execute_command(cmd)
|
|
|
|
|
|
|
|
return (rc, out, err)
|
|
|
|
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
# ===========================================
|
|
|
|
|
2012-07-23 07:56:48 +02:00
|
|
|
def main():
|
2012-10-20 07:00:31 +02:00
|
|
|
ssh_defaults = {
|
|
|
|
'bits': '2048',
|
|
|
|
'type': 'rsa',
|
|
|
|
'passphrase': None,
|
|
|
|
'comment': 'ansible-generated'
|
|
|
|
}
|
2012-07-23 07:56:48 +02:00
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec = dict(
|
|
|
|
state=dict(default='present', choices=['present', 'absent']),
|
2012-10-25 14:03:13 +02:00
|
|
|
name=dict(required=True, aliases=['user']),
|
2012-07-23 07:56:48 +02:00
|
|
|
uid=dict(default=None),
|
|
|
|
group=dict(default=None),
|
|
|
|
groups=dict(default=None),
|
|
|
|
comment=dict(default=None),
|
|
|
|
home=dict(default=None),
|
|
|
|
shell=dict(default=None),
|
|
|
|
password=dict(default=None),
|
|
|
|
# following options are specific to userdel
|
2012-10-13 00:06:36 +02:00
|
|
|
force=dict(default='no', choices=BOOLEANS),
|
|
|
|
remove=dict(default='no', choices=BOOLEANS),
|
2012-07-23 07:56:48 +02:00
|
|
|
# following options are specific to useradd
|
2012-10-13 00:06:36 +02:00
|
|
|
createhome=dict(default='yes', choices=BOOLEANS),
|
|
|
|
system=dict(default='no', choices=BOOLEANS),
|
2012-07-23 07:56:48 +02:00
|
|
|
# following options are specific to usermod
|
2012-10-20 07:00:31 +02:00
|
|
|
append=dict(default='no', choices=BOOLEANS),
|
|
|
|
# following are specific to ssh key generation
|
|
|
|
ssh_key=dict(choices=['generate']),
|
|
|
|
ssh_key_bits=dict(default=ssh_defaults['bits']),
|
|
|
|
ssh_key_type=dict(default=ssh_defaults['type']),
|
2012-10-29 23:00:58 +01:00
|
|
|
ssh_key_file=dict(default=None),
|
2012-10-20 07:00:31 +02:00
|
|
|
ssh_key_comment=dict(default=ssh_defaults['comment']),
|
|
|
|
ssh_key_passphrase=dict(default=None)
|
2012-07-23 07:56:48 +02:00
|
|
|
)
|
|
|
|
)
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
user = User(module)
|
2012-10-20 07:00:31 +02:00
|
|
|
|
2012-10-29 23:00:58 +01:00
|
|
|
if user.syslogging:
|
|
|
|
syslog.openlog('ansible-%s' % os.path.basename(__file__))
|
|
|
|
syslog.syslog(syslog.LOG_NOTICE, 'User instantiated - platform %s' % user.platform)
|
|
|
|
if user.distribution:
|
|
|
|
syslog.syslog(syslog.LOG_NOTICE, 'User instantiated - distribution %s' % user.distribution)
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
|
2012-07-23 07:56:48 +02:00
|
|
|
rc = None
|
|
|
|
out = ''
|
|
|
|
err = ''
|
|
|
|
result = {}
|
2012-10-29 23:00:58 +01:00
|
|
|
result['name'] = user.name
|
|
|
|
result['state'] = user.state
|
|
|
|
if user.state == 'absent':
|
|
|
|
if user.user_exists():
|
|
|
|
(rc, out, err) = user.remove_user()
|
2012-07-23 07:56:48 +02:00
|
|
|
if rc != 0:
|
|
|
|
module.fail_json(name=name, msg=err, rc=rc)
|
2012-10-29 23:00:58 +01:00
|
|
|
result['force'] = user.force
|
|
|
|
result['remove'] = user.remove
|
|
|
|
elif user.state == 'present':
|
|
|
|
if not user.user_exists():
|
|
|
|
(rc, out, err) = user.create_user()
|
|
|
|
result['system'] = user.system
|
|
|
|
result['createhome'] = user.createhome
|
2012-07-23 07:56:48 +02:00
|
|
|
else:
|
2012-10-29 23:00:58 +01:00
|
|
|
(rc, out, err) = user.modify_user()
|
|
|
|
result['append'] = user.append
|
2012-07-23 07:56:48 +02:00
|
|
|
if rc is not None and rc != 0:
|
2012-10-29 23:00:58 +01:00
|
|
|
module.fail_json(name=user.name, msg=err, rc=rc)
|
|
|
|
if user.password is not None:
|
2012-07-23 07:56:48 +02:00
|
|
|
result['password'] = 'NOT_LOGGING_PASSWORD'
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
|
2012-07-23 07:56:48 +02:00
|
|
|
if rc is None:
|
|
|
|
result['changed'] = False
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
else:
|
2012-07-23 07:56:48 +02:00
|
|
|
result['changed'] = True
|
|
|
|
if out:
|
|
|
|
result['stdout'] = out
|
|
|
|
if err:
|
|
|
|
result['stderr'] = err
|
2012-10-29 23:00:58 +01:00
|
|
|
|
|
|
|
if user.user_exists():
|
|
|
|
info = user.user_info()
|
2012-07-23 07:56:48 +02:00
|
|
|
if info == False:
|
2012-10-29 23:00:58 +01:00
|
|
|
result['msg'] = "failed to look up user name: %s" % user.name
|
2012-07-23 07:56:48 +02:00
|
|
|
result['failed'] = True
|
|
|
|
result['uid'] = info[2]
|
|
|
|
result['group'] = info[3]
|
|
|
|
result['comment'] = info[4]
|
|
|
|
result['home'] = info[5]
|
|
|
|
result['shell'] = info[6]
|
2012-10-29 23:00:58 +01:00
|
|
|
groups = user.user_group_membership()
|
2012-07-23 07:56:48 +02:00
|
|
|
result['uid'] = info[2]
|
2012-10-29 23:00:58 +01:00
|
|
|
if user.groups is not None:
|
|
|
|
result['groups'] = user.groups
|
|
|
|
|
|
|
|
# deal with ssh key
|
|
|
|
if user.sshkeygen:
|
|
|
|
(rc, out, err) = user.ssh_key_gen()
|
2012-10-20 07:00:31 +02:00
|
|
|
if rc is not None and rc != 0:
|
2012-10-29 23:00:58 +01:00
|
|
|
module.fail_json(name=user.name, msg=err, rc=rc)
|
2012-10-20 07:00:31 +02:00
|
|
|
if rc == 0:
|
|
|
|
result['changed'] = True
|
2012-10-29 23:00:58 +01:00
|
|
|
(rc, out, err) = user.ssh_key_fingerprint()
|
2012-10-20 07:00:31 +02:00
|
|
|
if rc == 0:
|
|
|
|
result['ssh_fingerprint'] = out.strip()
|
|
|
|
else:
|
|
|
|
result['ssh_fingerprint'] = err.strip()
|
2012-10-29 23:00:58 +01:00
|
|
|
result['ssh_key_file'] = user.get_ssh_key_path()
|
2012-10-20 07:00:31 +02:00
|
|
|
|
2012-07-23 07:56:48 +02:00
|
|
|
|
|
|
|
module.exit_json(**result)
|
Add user module to create, modify, and delete user accounts
This relies on useradd, usermod, and userdel utilities on the system.
The argument name is required; if state is not provided, present is
assumed. Other options supported for creating or modifying an existing
account: uid, gid, comment, home, shell, and password. If managing the
password, it must already be encrypted. When creating an account, you
can also provide the argument createhome to control whether the home
directory is created. Arguments supported for deleting an account are:
force (remove account even if user is logged in) and remove (remove home
directory).
2012-03-22 19:21:41 +01:00
|
|
|
|
2012-07-23 07:56:48 +02:00
|
|
|
# include magic from lib/ansible/module_common.py
|
|
|
|
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
|
|
|
main()
|