mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Backport win_useradd
Backported win_useradd.py from 2015.8 to fix issues with unicode, etc.
This commit is contained in:
parent
065f8c7fb3
commit
87282b6354
@ -1,10 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manage Windows users with the net user command
|
||||
Module for managing Windows Users
|
||||
|
||||
NOTE: This currently only works with local user accounts, not domain accounts
|
||||
:depends:
|
||||
- pywintypes
|
||||
- win32api
|
||||
- win32net
|
||||
- win32netcon
|
||||
- win32profile
|
||||
- win32security
|
||||
- win32ts
|
||||
|
||||
.. note::
|
||||
This currently only works with local user accounts, not domain accounts
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
try:
|
||||
from shlex import quote as _cmd_quote # pylint: disable=E0611
|
||||
@ -20,14 +32,16 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import pywintypes
|
||||
import wmi
|
||||
import pythoncom
|
||||
import pywintypes
|
||||
import win32api
|
||||
import win32con
|
||||
import win32net
|
||||
import win32netcon
|
||||
import win32profile
|
||||
import win32security
|
||||
import win32ts
|
||||
HAS_WIN32NET_MODS = True
|
||||
except ImportError:
|
||||
HAS_WIN32NET_MODS = False
|
||||
@ -40,33 +54,108 @@ def __virtual__():
|
||||
'''
|
||||
Set the user module if the kernel is Windows
|
||||
'''
|
||||
if HAS_WIN32NET_MODS is True and salt.utils.is_windows():
|
||||
if HAS_WIN32NET_MODS and salt.utils.is_windows():
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
||||
def _get_date_time_format(dt_string):
|
||||
'''
|
||||
Copied from win_system.py (_get_date_time_format)
|
||||
|
||||
Function that detects the date/time format for the string passed.
|
||||
|
||||
:param str dt_string:
|
||||
A date/time string
|
||||
|
||||
:return: The format of the passed dt_string
|
||||
:rtype: str
|
||||
'''
|
||||
valid_formats = [
|
||||
'%Y-%m-%d %I:%M:%S %p',
|
||||
'%m-%d-%y %I:%M:%S %p',
|
||||
'%m-%d-%Y %I:%M:%S %p',
|
||||
'%m/%d/%y %I:%M:%S %p',
|
||||
'%m/%d/%Y %I:%M:%S %p',
|
||||
'%Y/%m/%d %I:%M:%S %p',
|
||||
'%Y-%m-%d %I:%M:%S',
|
||||
'%m-%d-%y %I:%M:%S',
|
||||
'%m-%d-%Y %I:%M:%S',
|
||||
'%m/%d/%y %I:%M:%S',
|
||||
'%m/%d/%Y %I:%M:%S',
|
||||
'%Y/%m/%d %I:%M:%S',
|
||||
'%Y-%m-%d %I:%M %p',
|
||||
'%m-%d-%y %I:%M %p',
|
||||
'%m-%d-%Y %I:%M %p',
|
||||
'%m/%d/%y %I:%M %p',
|
||||
'%m/%d/%Y %I:%M %p',
|
||||
'%Y/%m/%d %I:%M %p',
|
||||
'%Y-%m-%d %I:%M',
|
||||
'%m-%d-%y %I:%M',
|
||||
'%m-%d-%Y %I:%M',
|
||||
'%m/%d/%y %I:%M',
|
||||
'%m/%d/%Y %I:%M',
|
||||
'%Y/%m/%d %I:%M',
|
||||
'%Y-%m-%d',
|
||||
'%m-%d-%y',
|
||||
'%m-%d-%Y',
|
||||
'%m/%d/%y',
|
||||
'%m/%d/%Y',
|
||||
'%Y/%m/%d',
|
||||
]
|
||||
for dt_format in valid_formats:
|
||||
try:
|
||||
datetime.strptime(dt_string, dt_format)
|
||||
return dt_format
|
||||
except ValueError:
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
def add(name,
|
||||
password=None,
|
||||
# Disable pylint checking on the next options. They exist to match the
|
||||
# user modules of other distributions.
|
||||
# pylint: disable=W0613
|
||||
uid=None,
|
||||
gid=None,
|
||||
groups=None,
|
||||
home=False,
|
||||
shell=None,
|
||||
unique=False,
|
||||
system=False,
|
||||
fullname=False,
|
||||
roomnumber=False,
|
||||
workphone=False,
|
||||
homephone=False,
|
||||
loginclass=False,
|
||||
createhome=False
|
||||
# pylint: enable=W0613
|
||||
):
|
||||
description=None,
|
||||
groups=None,
|
||||
home=None,
|
||||
homedrive=None,
|
||||
profile=None,
|
||||
logonscript=None):
|
||||
'''
|
||||
Add a user to the minion
|
||||
Add a user to the minion.
|
||||
|
||||
:param str name:
|
||||
User name
|
||||
|
||||
:param str password:
|
||||
User's password in plain text.
|
||||
|
||||
:param str fullname:
|
||||
The user's full name.
|
||||
|
||||
:param str description:
|
||||
A brief description of the user account.
|
||||
|
||||
:param list groups:
|
||||
A list of groups to add the user to.
|
||||
|
||||
:param str home:
|
||||
The path to the user's home directory.
|
||||
|
||||
:param str homedrive:
|
||||
The drive letter to assign to the home directory. Must be the Drive Letter
|
||||
followed by a colon. ie: U:
|
||||
|
||||
:param str profile:
|
||||
An explicit path to a profile. Can be a UNC or a folder on the system. If
|
||||
left blank, windows uses it's default profile directory.
|
||||
|
||||
:param str logonscript:
|
||||
Path to a login script to run when the user logs on.
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -74,28 +163,212 @@ def add(name,
|
||||
|
||||
salt '*' user.add name password
|
||||
'''
|
||||
if password:
|
||||
ret = __salt__['cmd.run_all']('net user {0} {1} /add /y'.format(name, password))
|
||||
user_info = {}
|
||||
if name:
|
||||
user_info['name'] = name
|
||||
else:
|
||||
ret = __salt__['cmd.run_all']('net user {0} /add'.format(name))
|
||||
if groups:
|
||||
chgroups(name, groups)
|
||||
return False
|
||||
user_info['password'] = password
|
||||
user_info['priv'] = win32netcon.USER_PRIV_USER
|
||||
user_info['home_dir'] = home
|
||||
user_info['comment'] = description
|
||||
user_info['flags'] = win32netcon.UF_SCRIPT
|
||||
user_info['script_path'] = logonscript
|
||||
|
||||
try:
|
||||
win32net.NetUserAdd(None, 1, user_info)
|
||||
except win32net.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to create user {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
update(name=name,
|
||||
homedrive=homedrive,
|
||||
profile=profile,
|
||||
fullname=fullname)
|
||||
|
||||
ret = chgroups(name, groups) if groups else True
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def update(name,
|
||||
password=None,
|
||||
fullname=None,
|
||||
description=None,
|
||||
home=None,
|
||||
homedrive=None,
|
||||
logonscript=None,
|
||||
profile=None,
|
||||
expiration_date=None,
|
||||
expired=None,
|
||||
account_disabled=None,
|
||||
unlock_account=None,
|
||||
password_never_expires=None,
|
||||
disallow_change_password=None):
|
||||
r'''
|
||||
Updates settings for the windows user. Name is the only required parameter.
|
||||
Settings will only be changed if the parameter is passed a value.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
:param str name:
|
||||
The user name to update.
|
||||
|
||||
:param str password:
|
||||
New user password in plain text.
|
||||
|
||||
:param str fullname:
|
||||
The user's full name.
|
||||
|
||||
:param str description:
|
||||
A brief description of the user account.
|
||||
|
||||
:param str home:
|
||||
The path to the user's home directory.
|
||||
|
||||
:param str homedrive:
|
||||
The drive letter to assign to the home directory. Must be the Drive Letter
|
||||
followed by a colon. ie: U:
|
||||
|
||||
:param str logonscript:
|
||||
The path to the logon script.
|
||||
|
||||
:param str profile:
|
||||
The path to the user's profile directory.
|
||||
|
||||
:param date expiration_date: The date and time when the account expires. Can
|
||||
be a valid date/time string. To set to never expire pass the string 'Never'.
|
||||
|
||||
:param bool expired: Pass `True` to expire the account. The user will be
|
||||
prompted to change their password at the next logon. Pass `False` to mark
|
||||
the account as 'not expired'. You can't use this to negate the expiration if
|
||||
the expiration was caused by the account expiring. You'll have to change
|
||||
the `expiration_date` as well.
|
||||
|
||||
:param bool account_disabled: True disables the account. False enables the
|
||||
account.
|
||||
|
||||
:param bool unlock_account: True unlocks a locked user account. False is
|
||||
ignored.
|
||||
|
||||
:param bool password_never_expires: True sets the password to never expire.
|
||||
False allows the password to expire.
|
||||
|
||||
:param bool disallow_change_password: True blocks the user from changing
|
||||
the password. False allows the user to change the password.
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.update bob password=secret profile=C:\Users\Bob
|
||||
home=\\server\homeshare\bob homedrive=U:
|
||||
'''
|
||||
|
||||
# Make sure the user exists
|
||||
# Return an object containing current settings for the user
|
||||
try:
|
||||
user_info = win32net.NetUserGetInfo(None, name, 4)
|
||||
except win32net.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to update user {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
# Check parameters to update
|
||||
# Update the user object with new settings
|
||||
if password:
|
||||
user_info['password'] = password
|
||||
if home:
|
||||
user_info['home_dir'] = home
|
||||
if homedrive:
|
||||
user_info['home_dir_drive'] = homedrive
|
||||
if description:
|
||||
user_info['comment'] = description
|
||||
if logonscript:
|
||||
user_info['script_path'] = logonscript
|
||||
if fullname:
|
||||
chfullname(name, fullname)
|
||||
return ret['retcode'] == 0
|
||||
user_info['full_name'] = fullname
|
||||
if profile:
|
||||
user_info['profile'] = profile
|
||||
if expiration_date:
|
||||
if expiration_date == 'Never':
|
||||
user_info['acct_expires'] = win32netcon.TIMEQ_FOREVER
|
||||
else:
|
||||
date_format = _get_date_time_format(expiration_date)
|
||||
if date_format:
|
||||
dt_obj = datetime.strptime(expiration_date, date_format)
|
||||
else:
|
||||
return 'Invalid start_date'
|
||||
user_info['acct_expires'] = time.mktime(dt_obj.timetuple())
|
||||
if expired is not None:
|
||||
if expired:
|
||||
user_info['password_expired'] = 1
|
||||
else:
|
||||
user_info['password_expired'] = 0
|
||||
if account_disabled is not None:
|
||||
if account_disabled:
|
||||
user_info['flags'] |= win32netcon.UF_ACCOUNTDISABLE
|
||||
else:
|
||||
user_info['flags'] ^= win32netcon.UF_ACCOUNTDISABLE
|
||||
if unlock_account is not None:
|
||||
if unlock_account:
|
||||
user_info['flags'] ^= win32netcon.UF_LOCKOUT
|
||||
if password_never_expires is not None:
|
||||
if password_never_expires:
|
||||
user_info['flags'] |= win32netcon.UF_DONT_EXPIRE_PASSWD
|
||||
else:
|
||||
user_info['flags'] ^= win32netcon.UF_DONT_EXPIRE_PASSWD
|
||||
if disallow_change_password is not None:
|
||||
if disallow_change_password:
|
||||
user_info['flags'] |= win32netcon.UF_PASSWD_CANT_CHANGE
|
||||
else:
|
||||
user_info['flags'] ^= win32netcon.UF_PASSWD_CANT_CHANGE
|
||||
|
||||
# Apply new settings
|
||||
try:
|
||||
win32net.NetUserSetInfo(None, name, 4, user_info)
|
||||
except win32net.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to update user {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def delete(name,
|
||||
# Disable pylint checking on the next options. They exist to match
|
||||
# the user modules of other distributions.
|
||||
# pylint: disable=W0613
|
||||
purge=False,
|
||||
force=False
|
||||
# pylint: enable=W0613
|
||||
):
|
||||
force=False):
|
||||
'''
|
||||
Remove a user from the minion
|
||||
NOTE: purge and force have not been implemented on Windows yet
|
||||
|
||||
:param str name:
|
||||
The name of the user to delete
|
||||
|
||||
:param bool purge:
|
||||
Boolean value indicating that the user profile should also be removed when
|
||||
the user account is deleted. If set to True the profile will be removed.
|
||||
|
||||
:param bool force:
|
||||
Boolean value indicating that the user account should be deleted even if the
|
||||
user is logged in. True will log the user out and delete user.
|
||||
|
||||
:return:
|
||||
True if successful
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -103,35 +376,150 @@ def delete(name,
|
||||
|
||||
salt '*' user.delete name
|
||||
'''
|
||||
ret = __salt__['cmd.run_all']('net user {0} /delete'.format(name))
|
||||
return ret['retcode'] == 0
|
||||
# Check if the user exists
|
||||
try:
|
||||
user_info = win32net.NetUserGetInfo(None, name, 4)
|
||||
except win32net.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('User not found: {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
# Check if the user is logged in
|
||||
# Return a list of logged in users
|
||||
try:
|
||||
sess_list = win32ts.WTSEnumerateSessions()
|
||||
except win32ts.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('No logged in users found')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
|
||||
# Is the user one that is logged in
|
||||
logged_in = False
|
||||
session_id = None
|
||||
for sess in sess_list:
|
||||
if win32ts.WTSQuerySessionInformation(None, sess['SessionId'], win32ts.WTSUserName) == name:
|
||||
session_id = sess['SessionId']
|
||||
logged_in = True
|
||||
|
||||
# If logged in and set to force, log the user out and continue
|
||||
# If logged in and not set to force, return false
|
||||
if logged_in:
|
||||
if force:
|
||||
try:
|
||||
win32ts.WTSLogoffSession(win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True)
|
||||
except win32ts.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('User not found: {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
else:
|
||||
log.error('User {0} is currently logged in.'.format(name))
|
||||
return False
|
||||
|
||||
# Remove the User Profile directory
|
||||
if purge:
|
||||
try:
|
||||
sid = getUserSid(name)
|
||||
win32profile.DeleteProfile(sid)
|
||||
except pywintypes.error as exc:
|
||||
(number, context, message) = exc
|
||||
if number == 2: # Profile Folder Not Found
|
||||
pass
|
||||
else:
|
||||
log.error('Failed to remove profile for {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
# And finally remove the user account
|
||||
try:
|
||||
win32net.NetUserDel(None, name)
|
||||
except win32net.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to delete user {0}'.format(name))
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def setpassword(name, password):
|
||||
def getUserSid(username):
|
||||
'''
|
||||
Set a user's password
|
||||
Get the Security ID for the user
|
||||
|
||||
:param str username:
|
||||
user name for which to look up the SID
|
||||
|
||||
:return:
|
||||
Returns the user SID
|
||||
:rtype: str
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.setpassword name password
|
||||
salt '*' user.getUserSid jsnuffy
|
||||
'''
|
||||
ret = __salt__['cmd.run_all'](
|
||||
'net user {0} {1}'.format(name, password), output_loglevel='quiet'
|
||||
)
|
||||
return ret['retcode'] == 0
|
||||
domain = win32api.GetComputerName()
|
||||
if username.find(u'\\') != -1:
|
||||
domain = username.split(u'\\')[0]
|
||||
username = username.split(u'\\')[-1]
|
||||
domain = domain.upper()
|
||||
return win32security.ConvertSidToStringSid(win32security.LookupAccountName(None, domain + u'\\' + username)[0])
|
||||
|
||||
|
||||
def setpassword(name, password):
|
||||
'''
|
||||
Set the user's password
|
||||
|
||||
:param str name:
|
||||
user name for which to set the password
|
||||
|
||||
:param str password:
|
||||
the new password
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.setpassword jsnuffy sup3rs3cr3t
|
||||
'''
|
||||
return update(name=name, password=password)
|
||||
|
||||
|
||||
def addgroup(name, group):
|
||||
'''
|
||||
Add user to a group
|
||||
|
||||
:param str name:
|
||||
user name to add to the group
|
||||
|
||||
:param str group:
|
||||
name of the group to which to add the user
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.addgroup username groupname
|
||||
salt '*' user.addgroup jsnuffy 'Power Users'
|
||||
'''
|
||||
name = _cmd_quote(name)
|
||||
group = _cmd_quote(group).lstrip('\'').rstrip('\'')
|
||||
@ -152,11 +540,21 @@ def removegroup(name, group):
|
||||
'''
|
||||
Remove user from a group
|
||||
|
||||
:param str name:
|
||||
user name to remove from the group
|
||||
|
||||
:param str group:
|
||||
name of the group from which to remove the user
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.removegroup username groupname
|
||||
salt '*' user.removegroup jsnuffy 'Power Users'
|
||||
'''
|
||||
name = _cmd_quote(name)
|
||||
group = _cmd_quote(group).lstrip('\'').rstrip('\'')
|
||||
@ -180,6 +578,19 @@ def chhome(name, home, persist=False):
|
||||
Change the home directory of the user, pass True for persist to move files
|
||||
to the new home directory if the old home directory exist.
|
||||
|
||||
:param str name:
|
||||
name of the user whose home directory you wish to change
|
||||
|
||||
:param str home:
|
||||
new location of the home directory
|
||||
|
||||
:param bool persist:
|
||||
True to move the contents of the existing home directory to the new location
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -194,8 +605,7 @@ def chhome(name, home, persist=False):
|
||||
if home == pre_info['home']:
|
||||
return True
|
||||
|
||||
if __salt__['cmd.retcode']('net user {0} /homedir:{1}'.format(
|
||||
name, home)) != 0:
|
||||
if not update(name=name, home=home):
|
||||
return False
|
||||
|
||||
if persist and home is not None and pre_info['home'] is not None:
|
||||
@ -214,59 +624,46 @@ def chprofile(name, profile):
|
||||
'''
|
||||
Change the profile directory of the user
|
||||
|
||||
:param str name:
|
||||
name of the user whose profile you wish to change
|
||||
|
||||
:param str profile:
|
||||
new location of the profile
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.chprofile foo \\\\fileserver\\profiles\\foo
|
||||
'''
|
||||
pre_info = info(name)
|
||||
|
||||
if not pre_info:
|
||||
return False
|
||||
|
||||
if profile == pre_info['profile']:
|
||||
return True
|
||||
if __salt__['cmd.retcode']('net user {0} /profilepath:{1}'.format(
|
||||
name, profile)) != 0:
|
||||
return False
|
||||
|
||||
post_info = info(name)
|
||||
if post_info['profile'] != pre_info['profile']:
|
||||
return post_info['profile'] == profile
|
||||
|
||||
return False
|
||||
return update(name=name, profile=profile)
|
||||
|
||||
|
||||
def chfullname(name, fullname):
|
||||
'''
|
||||
Change the full name of the user
|
||||
|
||||
:param str name:
|
||||
user name for which to change the full name
|
||||
|
||||
:param str fullname:
|
||||
the new value for the full name
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.chfullname user 'First Last'
|
||||
'''
|
||||
pre_info = info(name)
|
||||
|
||||
if not pre_info:
|
||||
return False
|
||||
|
||||
name = _cmd_quote(name)
|
||||
fullname_qts = _cmd_quote(fullname).replace("'", "\"")
|
||||
|
||||
if fullname == pre_info['fullname']:
|
||||
return True
|
||||
if __salt__['cmd.retcode']('net user {0} /fullname:{1}'.format(
|
||||
name, fullname_qts), python_shell=True) != 0:
|
||||
return False
|
||||
|
||||
post_info = info(name)
|
||||
if post_info['fullname'] != pre_info['fullname']:
|
||||
return post_info['fullname'] == fullname
|
||||
|
||||
return False
|
||||
return update(name=name, fullname=fullname)
|
||||
|
||||
|
||||
def chgroups(name, groups, append=True):
|
||||
@ -274,11 +671,26 @@ def chgroups(name, groups, append=True):
|
||||
Change the groups this user belongs to, add append=False to make the user a
|
||||
member of only the specified groups
|
||||
|
||||
:param str name:
|
||||
user name for which to change groups
|
||||
|
||||
:param groups:
|
||||
a single group or a list of groups to assign to the user
|
||||
:type groups: list, str
|
||||
|
||||
:param bool append:
|
||||
True adds the passed groups to the user's current groups
|
||||
False sets the user's groups to the passed groups only
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.chgroups foo wheel,root True
|
||||
salt '*' user.chgroups jsnuffy Administrators,Users True
|
||||
'''
|
||||
if isinstance(groups, string_types):
|
||||
groups = groups.split(',')
|
||||
@ -312,11 +724,39 @@ def info(name):
|
||||
'''
|
||||
Return user information
|
||||
|
||||
:param str name:
|
||||
Username for which to display information
|
||||
|
||||
:returns:
|
||||
A dictionary containing user information
|
||||
- fullname
|
||||
- username
|
||||
- SID
|
||||
- passwd (will always return None)
|
||||
- comment (same as description, left here for backwards compatibility)
|
||||
- description
|
||||
- active
|
||||
- logonscript
|
||||
- profile
|
||||
- home
|
||||
- homedrive
|
||||
- groups
|
||||
- password_changed
|
||||
- successful_logon_attempts
|
||||
- failed_logon_attempts
|
||||
- last_logon
|
||||
- account_disabled
|
||||
- account_locked
|
||||
- password_never_expires
|
||||
- disallow_change_password
|
||||
- gid
|
||||
:rtype: dict
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.info root
|
||||
salt '*' user.info jsnuffy
|
||||
'''
|
||||
ret = {}
|
||||
items = {}
|
||||
@ -337,18 +777,54 @@ def info(name):
|
||||
ret['uid'] = win32security.ConvertSidToStringSid(items['user_sid'])
|
||||
ret['passwd'] = items['password']
|
||||
ret['comment'] = items['comment']
|
||||
ret['description'] = items['comment']
|
||||
ret['active'] = (not bool(items['flags'] & win32netcon.UF_ACCOUNTDISABLE))
|
||||
ret['logonscript'] = items['script_path']
|
||||
ret['profile'] = items['profile']
|
||||
ret['failed_logon_attempts'] = items['bad_pw_count']
|
||||
ret['successful_logon_attempts'] = items['num_logons']
|
||||
secs = time.mktime(datetime.now().timetuple()) - items['password_age']
|
||||
ret['password_changed'] = datetime.fromtimestamp(secs). \
|
||||
strftime('%Y-%m-%d %H:%M:%S')
|
||||
if items['last_logon'] == 0:
|
||||
ret['last_logon'] = 'Never'
|
||||
else:
|
||||
ret['last_logon'] = datetime.fromtimestamp(items['last_logon']).\
|
||||
strftime('%Y-%m-%d %H:%M:%S')
|
||||
ret['expiration_date'] = datetime.fromtimestamp(items['acct_expires']).\
|
||||
strftime('%Y-%m-%d %H:%M:%S')
|
||||
ret['expired'] = items['password_expired'] == 1
|
||||
if not ret['profile']:
|
||||
ret['profile'] = _get_userprofile_from_registry(name, ret['uid'])
|
||||
ret['home'] = items['home_dir']
|
||||
ret['homedrive'] = items['home_dir_drive']
|
||||
if not ret['home']:
|
||||
ret['home'] = ret['profile']
|
||||
ret['groups'] = groups
|
||||
if items['flags'] & win32netcon.UF_DONT_EXPIRE_PASSWD == 0:
|
||||
ret['password_never_expires'] = False
|
||||
else:
|
||||
ret['password_never_expires'] = True
|
||||
if items['flags'] & win32netcon.UF_ACCOUNTDISABLE == 0:
|
||||
ret['account_disabled'] = False
|
||||
else:
|
||||
ret['account_disabled'] = True
|
||||
if items['flags'] & win32netcon.UF_LOCKOUT == 0:
|
||||
ret['account_locked'] = False
|
||||
else:
|
||||
ret['account_locked'] = True
|
||||
if items['flags'] & win32netcon.UF_PASSWD_CANT_CHANGE == 0:
|
||||
ret['disallow_change_password'] = False
|
||||
else:
|
||||
ret['disallow_change_password'] = True
|
||||
|
||||
ret['gid'] = ''
|
||||
|
||||
return ret
|
||||
return ret
|
||||
|
||||
else:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _get_userprofile_from_registry(user, sid):
|
||||
@ -369,6 +845,13 @@ def list_groups(name):
|
||||
'''
|
||||
Return a list of groups the named user belongs to
|
||||
|
||||
:param str name:
|
||||
user name for which to list groups
|
||||
|
||||
:return:
|
||||
list of groups to which the user belongs
|
||||
:rtype: list
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -390,6 +873,14 @@ def getent(refresh=False):
|
||||
'''
|
||||
Return the list of all info for all users
|
||||
|
||||
:param bool refresh:
|
||||
Refresh the cached user information. Default is False. Useful when used from
|
||||
within a state function.
|
||||
|
||||
:return:
|
||||
A dictionary containing information about all users on the system
|
||||
:rtype: dict
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
@ -421,6 +912,16 @@ def getent(refresh=False):
|
||||
def list_users():
|
||||
'''
|
||||
Return a list of users on Windows
|
||||
|
||||
:return:
|
||||
list of users on the system
|
||||
:rtype: list
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.list_users
|
||||
'''
|
||||
res = 0
|
||||
users = []
|
||||
@ -447,11 +948,21 @@ def rename(name, new_name):
|
||||
'''
|
||||
Change the username for a named user
|
||||
|
||||
:param str name:
|
||||
user name to change
|
||||
|
||||
:param str new_name:
|
||||
the new name for the current user
|
||||
|
||||
:return:
|
||||
True if successful. False is unsuccessful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.rename name new_name
|
||||
salt '*' user.rename jsnuffy jshmoe
|
||||
'''
|
||||
# Load information for the current name
|
||||
current_info = info(name)
|
||||
|
Loading…
Reference in New Issue
Block a user