Merge pull request #43097 from twangboy/win_fix_group

Fix `group.present` for Windows
This commit is contained in:
Nicole Thomas 2017-08-23 09:19:55 -04:00 committed by GitHub
commit e9ccaa61d2
3 changed files with 196 additions and 59 deletions

View file

@ -12,6 +12,7 @@ from __future__ import absolute_import
# Import salt libs
import salt.utils
import salt.utils.win_functions
try:
@ -35,10 +36,18 @@ def __virtual__():
return (False, "Module win_groupadd: module only works on Windows systems")
def add(name, gid=None, system=False):
def add(name, **kwargs):
'''
Add the specified group
Args:
name (str):
The name of the group to add
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
@ -57,29 +66,32 @@ def add(name, gid=None, system=False):
compObj = nt.GetObject('', 'WinNT://.,computer')
newGroup = compObj.Create('group', name)
newGroup.SetInfo()
ret['changes'].append((
'Successfully created group {0}'
).format(name))
ret['changes'].append('Successfully created group {0}'.format(name))
except pywintypes.com_error as com_err:
ret['result'] = False
if len(com_err.excepinfo) >= 2:
friendly_error = com_err.excepinfo[2].rstrip('\r\n')
ret['comment'] = (
'Failed to create group {0}. {1}'
).format(name, friendly_error)
ret['comment'] = 'Failed to create group {0}. {1}' \
''.format(name, friendly_error)
else:
ret['result'] = None
ret['comment'] = (
'The group {0} already exists.'
).format(name)
ret['comment'] = 'The group {0} already exists.'.format(name)
return ret
def delete(name):
def delete(name, **kwargs):
'''
Remove the named group
Args:
name (str):
The name of the group to remove
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
@ -118,6 +130,14 @@ def info(name):
'''
Return information about a group
Args:
name (str):
The name of the group for which to get information
Returns:
dict: A dictionary of information about the group
CLI Example:
.. code-block:: bash
@ -151,6 +171,17 @@ def getent(refresh=False):
'''
Return info on all groups
Args:
refresh (bool):
Refresh the info for all groups in ``__context__``. If False only
the groups in ``__context__`` wil be returned. If True the
``__context__`` will be refreshed with current data and returned.
Default is False
Returns:
A list of groups and their information
CLI Example:
.. code-block:: bash
@ -182,16 +213,26 @@ def getent(refresh=False):
return ret
def adduser(name, username):
def adduser(name, username, **kwargs):
'''
add a user to a group
Add a user to a group
Args:
name (str):
The name of the group to modify
username (str):
The name of the user to add to the group
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
salt '*' group.adduser foo username
'''
ret = {'name': name,
@ -209,7 +250,7 @@ def adduser(name, username):
'/', '\\').encode('ascii', 'backslashreplace').lower())
try:
if __fixlocaluser(username.lower()) not in existingMembers:
if salt.utils.win_functions.get_sam_name(username) not in existingMembers:
if not __opts__['test']:
groupObj.Add('WinNT://' + username.replace('\\', '/'))
@ -231,16 +272,26 @@ def adduser(name, username):
return ret
def deluser(name, username):
def deluser(name, username, **kwargs):
'''
remove a user from a group
Remove a user from a group
Args:
name (str):
The name of the group to modify
username (str):
The name of the user to remove from the group
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
salt '*' group.deluser foo username
'''
ret = {'name': name,
@ -258,7 +309,7 @@ def deluser(name, username):
'/', '\\').encode('ascii', 'backslashreplace').lower())
try:
if __fixlocaluser(username.lower()) in existingMembers:
if salt.utils.win_functions.get_sam_name(username) in existingMembers:
if not __opts__['test']:
groupObj.Remove('WinNT://' + username.replace('\\', '/'))
@ -280,16 +331,27 @@ def deluser(name, username):
return ret
def members(name, members_list):
def members(name, members_list, **kwargs):
'''
remove a user from a group
Ensure a group contains only the members in the list
Args:
name (str):
The name of the group to modify
members_list (str):
A single user or a comma separated list of users. The group will
contain only the users specified in this list.
Returns:
dict: A dictionary of results
CLI Example:
.. code-block:: bash
salt '*' group.members foo 'user1,user2,user3'
'''
ret = {'name': name,
@ -297,7 +359,7 @@ def members(name, members_list):
'changes': {'Users Added': [], 'Users Removed': []},
'comment': []}
members_list = [__fixlocaluser(thisMember) for thisMember in members_list.lower().split(",")]
members_list = [salt.utils.win_functions.get_sam_name(m) for m in members_list.split(",")]
if not isinstance(members_list, list):
ret['result'] = False
ret['comment'].append('Members is not a list object')
@ -364,27 +426,26 @@ def members(name, members_list):
return ret
def __fixlocaluser(username):
'''
prefixes a username w/o a backslash with the computername
i.e. __fixlocaluser('Administrator') would return 'computername\administrator'
'''
if '\\' not in username:
username = ('{0}\\{1}').format(__salt__['grains.get']('host'), username)
return username.lower()
def list_groups(refresh=False):
'''
Return a list of groups
Args:
refresh (bool):
Refresh the info for all groups in ``__context__``. If False only
the groups in ``__context__`` wil be returned. If True, the
``__context__`` will be refreshed with current data and returned.
Default is False
Returns:
list: A list of groups on the machine
CLI Example:
.. code-block:: bash
salt '*' group.getent
salt '*' group.list_groups
'''
if 'group.list_groups' in __context__ and not refresh:
return __context__['group.getent']

View file

@ -1,10 +1,15 @@
# -*- coding: utf-8 -*-
'''
r'''
Management of user groups
=========================
The group module is used to create and manage unix group settings, groups
can be either present or absent:
The group module is used to create and manage group settings, groups can be
either present or absent. User/Group names can be passed to the ``adduser``,
``deluser``, and ``members`` parameters. ``adduser`` and ``deluser`` can be used
together but not with ``members``.
In Windows, if no domain is specified in the user or group name (ie:
`DOMAIN\username``) the module will assume a local user or group.
.. code-block:: yaml
@ -36,6 +41,10 @@ import sys
# Import 3rd-party libs
import salt.ext.six as six
# Import Salt libs
import salt.utils
import salt.utils.win_functions
def _changes(name,
gid=None,
@ -50,6 +59,18 @@ def _changes(name,
if not lgrp:
return False
# User and Domain names are not case sensitive in Windows. Let's make them
# all lower case so we can compare properly
if salt.utils.is_windows():
if lgrp['members']:
lgrp['members'] = [user.lower() for user in lgrp['members']]
if members:
members = [salt.utils.win_functions.get_sam_name(user) for user in members]
if addusers:
addusers = [salt.utils.win_functions.get_sam_name(user) for user in addusers]
if delusers:
delusers = [salt.utils.win_functions.get_sam_name(user) for user in delusers]
change = {}
if gid:
if lgrp['gid'] != gid:
@ -57,7 +78,7 @@ def _changes(name,
if members:
# -- if new member list if different than the current
if set(lgrp['members']) ^ set(members):
if set(lgrp['members']).symmetric_difference(members):
change['members'] = members
if addusers:
@ -79,31 +100,58 @@ def present(name,
addusers=None,
delusers=None,
members=None):
'''
r'''
Ensure that a group is present
name
The name of the group to manage
Args:
gid
The group id to assign to the named group; if left empty, then the next
available group id will be assigned
name (str):
The name of the group to manage
system
Whether or not the named group is a system group. This is essentially
the '-r' option of 'groupadd'.
gid (str):
The group id to assign to the named group; if left empty, then the
next available group id will be assigned. Ignored on Windows
addusers
List of additional users to be added as a group members.
system (bool):
Whether or not the named group is a system group. This is essentially
the '-r' option of 'groupadd'. Ignored on Windows
delusers
Ensure these user are removed from the group membership.
addusers (list):
List of additional users to be added as a group members. Cannot
conflict with names in delusers. Cannot be used in conjunction with
members.
members
Replace existing group members with a list of new members.
delusers (list):
Ensure these user are removed from the group membership. Cannot
conflict with names in addusers. Cannot be used in conjunction with
members.
Note: Options 'members' and 'addusers/delusers' are mutually exclusive and
can not be used together.
members (list):
Replace existing group members with a list of new members. Cannot be
used in conjunction with addusers or delusers.
Example:
.. code-block:: yaml
# Adds DOMAIN\db_admins and Administrators to the local db_admin group
# Removes Users
db_admin:
group.present:
- addusers:
- DOMAIN\db_admins
- Administrators
- delusers:
- Users
# Ensures only DOMAIN\domain_admins and the local Administrator are
# members of the local Administrators group. All other users are
# removed
Administrators:
group.present:
- members:
- DOMAIN\domain_admins
- Administrator
'''
ret = {'name': name,
'changes': {},
@ -233,8 +281,17 @@ def absent(name):
'''
Ensure that the named group is absent
name
The name of the group to remove
Args:
name (str):
The name of the group to remove
Example:
.. code-block:: yaml
# Removes the local group `db_admin`
db_admin:
group.absent
'''
ret = {'name': name,
'changes': {},

View file

@ -4,6 +4,9 @@ Various functions to be used by windows during start up and to monkey patch
missing functions in other modules
'''
from __future__ import absolute_import
import platform
# Import Salt Libs
from salt.exceptions import CommandExecutionError
# Import 3rd Party Libs
@ -138,3 +141,19 @@ def get_current_user():
return False
return user_name
def get_sam_name(username):
'''
Gets the SAM name for a user. It basically prefixes a username without a
backslash with the computer name. If the username contains a backslash, it
is returned as is.
Everything is returned lower case
i.e. salt.utils.fix_local_user('Administrator') would return 'computername\administrator'
'''
if '\\' not in username:
username = '{0}\\{1}'.format(platform.node(), username)
return username.lower()