# Account Management Providers
#
# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the FreeBSD Project.
#
#
# Authors: Roman Rakus <rrakus@redhat.com>
# Authors: Jan Safranek <jsafrane@redhat.com>
#
"""
LMI account provider client library.
This set of functions can create, modify and delete users and groups on
a remote managed system.
"""
from lmi.scripts.common.errors import LmiFailed
from lmi.shell.LMIInstanceName import LMIInstanceName
from lmi.shell.LMIInstance import LMIInstance
from lmi.scripts.common import get_logger
try:
import lmiwbem as wbem
except ImportError:
import pywbem as wbem
import lmi.scripts.common
LOG = get_logger(__name__)
[docs]def list_users(ns):
"""
Yield all users on the system.
:rtype: generator of LMIInstances.
"""
for user in ns.LMI_Account.instances():
yield user
[docs]def list_groups(ns):
"""
Yield all groups on the system.
:rtype: generator of LMIInstances.
"""
for group in ns.LMI_Group.instances():
yield group
[docs]def get_user(ns, username):
"""
Return LMIInstance of the user. This function raises LmiFailed if the user
is not found.
:type username: string
:param username: Name of the user.
:rtype: LMIInstance of LMI_Account
:returns: The user.
"""
cs = lmi.scripts.common.get_computer_system(ns)
keys = {
'Name': username,
'CreationClassName': 'LMI_Account',
'SystemCreationClassName': cs.CreationClassName,
'SystemName': cs.Name,
}
try:
user = ns.LMI_Account.new_instance_name(keys).to_instance()
except wbem.CIMError, err:
if err[0] == wbem.CIM_ERR_NOT_FOUND:
raise LmiFailed("Cannot find the user: %s" % username)
raise
return user
[docs]def get_group(ns, groupname):
"""
Return LMIInstance of the group. This function raises LmiFailed if the user
is not found.
:type groupname: string
:param groupname: Name of the group.
:rtype: LMIInstance of LMI_Group
:returns: The group.
"""
keys = {'Name' : groupname, 'CreationClassName': 'LMI_Group'}
try:
group = ns.LMI_Group.new_instance_name(keys).to_instance()
except wbem.CIMError, err:
if err[0] == wbem.CIM_ERR_NOT_FOUND:
raise LmiFailed("Cannot find the group: %s" % groupname)
raise
return group
[docs]def delete_user(ns, user,
no_delete_group=False,
no_delete_home=False,
force=False):
"""
Delete a user.
:type user: LMIInstance or LMIInstanceName of LMI_Account.
:param user: User to delete.
:type no_delete_group: boolean
:param no_delete_group: True, if the user's private group should be preserved.
(default = False, the group is deleted).
:type no_delete_home: boolean
:param no_delete_home: True, if user's home directory should be preserved.
(default = False, home is deleted).
:type force: boolean
:param force: True, if the home directory should be remove even though the
user is not owner of the directory. (default = False, do not remove
user's home if it is owned by someone else).
"""
params = {
"DontDeleteHomeDirectory": no_delete_home,
"DontDeleteGroup": no_delete_group,
"Force": force
}
for key in params.keys():
if params[key] is None:
del params[key]
name = user.Name
uid = user.UserID
LOG().debug("Removing user %s with arguments %s", name, str(params))
user.DeleteUser(**params)
LOG().info("Removed user %s (id=%s).", name, uid)
[docs]def create_user(ns, name,
gecos=None,
home=None, create_home=True,
shell=None,
uid=None,
gid=None, create_group=True,
reserved=False,
password=None, plain_password=False,
):
"""
Create a new user.
:type name: string
:param name: Name of the user.
:type gecos: string
:param gecos: GECOS information of the new user.
:type home: string
:param home: Home directory.
:type create_home: boolean
:param create_home: True, if home directory should be automatically created.
:type shell: string
:param shell: User's shell.
:type uid: int
:param uid: Desired UID. If None, system will allocate a free one.
:type gid: int
:param gid: Desired GID. If None, system will allocate a free one.
:type create_group: boolean
:param create_group: True, if user's private group should be created.
:type reserved: boolean
:param reserved: True, if the account is system one, i.e. it's UID will
be allocated in system account space (below system defined
threshold). (default=False, the account is an user).
:type password: string
:param password: User password.
:type plain_password: boolean
:param plain_password: True, if the provided password is plain text string,
False if it is already hashed by crypt().
:rtype: LMIInstanceName
:returns: Created used.
"""
cs = lmi.scripts.common.get_computer_system(ns)
lams = ns.LMI_AccountManagementService.first_instance()
params = dict({
"Name": name,
"System": cs,
"GECOS": gecos,
"HomeDirectory": home,
"DontCreateHome": not create_home,
"Shell": shell,
"UID": int(uid) if uid else uid,
"GID": int(gid) if gid else gid,
"SystemAccount": reserved,
"Password": password,
"DontCreateGroup": not create_group,
"PasswordIsPlain": plain_password
})
for key in params.keys():
if params[key] is None:
del params[key]
LOG().debug("Creating user %s with arguments %s", name, str(params))
(ret, outparams, err) = lams.CreateAccount(**params)
if ret != 0:
if err:
raise LmiFailed("Cannot create the user: %s." % err)
values = lams.CreateAccount.CreateAccountValues
raise LmiFailed("Cannot create the user: %s."
% (values.value_name(ret),))
account = outparams["Account"]
LOG().info(u"Created %suser \"%s\" with id %s.",
"system " if reserved else "", name,
account.to_instance().UserID)
return account
[docs]def get_users_in_group(ns, group):
"""
Yields users in given group.
:type group: LMIInstance or LMIInstanceName of LMI_Group.
:param group: The group to inspect.
:returns: Generator of LMIInstances of LMI_Account.
"""
for identity in group.associators(
AssocClass="LMI_MemberOfGroup", ResultClass="LMI_Identity"):
yield identity.first_associator(
AssocClass="LMI_AssignedAccountIdentity",
ResultClass="LMI_Account")
[docs]def create_group(ns, group, reserved=False, gid=None):
"""
Create a new group on the system.
:type group: string
:param group: Name of the group.
:type reserved: boolean
:param reserved: Whether the group is a system one (its GID will be
allocated lower than system-defined threshold).
:type gid: int
:param gid: Required GID. It will be allocated automatically if it's None.
:rtype: LMIInstanceName of the created group.
:returns: Created group.
"""
cs = lmi.scripts.common.get_computer_system(ns)
lams = ns.LMI_AccountManagementService.first_instance()
params = {
"Name" : group,
"System" : cs}
if reserved:
params["SystemAccount"] = True
if gid is not None:
params["GID"] = int(gid)
LOG().debug("Creating group %s with arguments %s", group, str(params))
(ret, outparams, err) = lams.CreateGroup(**params)
if ret != 0:
if err:
raise LmiFailed("Cannot create the group: %s." % err)
values = lams.CreateGroup.CreateGroupValues
raise LmiFailed("Cannot create the group: %s."
% (values.value_name(ret),))
group = outparams["Group"]
LOG().info(u"Created %sgroup \"%s\" with id %s.",
"system " if reserved else "", group.Name,
group.to_instance().InstanceID)
return group
[docs]def delete_group(ns, group):
"""
Delete a group.
:type group: LMIInstance or LMIInstanceName of LMI_Group.
:param group: The group to delete.
"""
if not isinstance(group, LMIInstance):
group = group.to_instnace()
name = group.Name
gid = group.InstanceID
LOG().debug("Removing group %s (id=%s)", name, gid)
group.delete()
LOG().info("Removed group %s (id=%s)", name, gid)
[docs]def is_in_group(group, user):
"""
Return True if user is in group
:type group: LMIInstance or LMIInstanceName of LMI_Group.
:param group: The group.
:type user: LMIInstance or LMIInstanceName of LMI_Account.
:param user: User to check.
"""
if isinstance(user, LMIInstanceName):
user_inst = user.to_instance()
if not user_inst:
raise LmiFailed("Cannot find the user: %s." % user.Name)
user = user_inst
identity = group.first_associator(
AssocClass="LMI_MemberOfGroup",
ResultClass="LMI_Identity")
if identity is None:
return False
return identity.InstanceID.split(":")[-1] == user.UserID
[docs]def add_to_group(ns, group, users):
"""
Add users to a group.
:type group: LMIInstance or LMIInstanceName of LMI_Group.
:param group: The group.
:type users: List (or generator) of LMIInstances or LMIInstanceNames of
LMI_Account.
:param users: Users to add.
"""
for user in users:
if is_in_group(group, user):
LOG().info('User "%s" already is in group "%s", skipping.',
user.Name, group.Name)
else:
# get identity
identity = user.first_associator(
AssocClass="LMI_AssignedAccountIdentity",
ResultClass="LMI_Identity")
# add the user; create instance of LMI_MemberOfGroup
LOG().debug('Adding user %s to group %s.', user.Name, group.Name)
ns.LMI_MemberOfGroup.create_instance(
{"Member":identity.path, "Collection":group.path})
LOG().info('User %s now belongs to group %s.', user.Name, group.Name)
[docs]def remove_from_group(ns, group, users):
"""
Remove users from a group.
:type group: LMIInstance or LMIInstanceName of LMI_Group.
:param group: The group.
:type users: List (or generator) of LMIInstances or LMIInstanceNames of
LMI_Account.
:param users: Users to remove.
"""
for user in users:
removed = False
# get identity
identity = user.first_associator(
AssocClass="LMI_AssignedAccountIdentity",
ResultClass="LMI_Identity")
# get MemberOfGroup
for mog in identity.references(ResultClass="LMI_MemberOfGroup"):
if mog.Collection.Name == group.Name:
LOG().debug('Removing user %s from group %s.', user.Name, group.Name)
mog.delete()
LOG().info('User %s removed from group %s.', user.Name, group.Name)
removed = True
if not removed:
LOG().error('User %s does not belong to group %s!', user.Name, group.Name)