Forked from
DM / dm-docs
261 commits behind, 786 commits ahead of the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ldapUserManager.py 18.30 KiB
#!/usr/bin/env python
import ldap
import ldap.modlist
import copy
import re
import types
from base64 import b16encode
from base64 import b16decode
from base64 import b64encode
from base64 import b64decode
from dm.common.objects.ldapUserInfo import LdapUserInfo
from dm.common.utility.loggingManager import LoggingManager
from dm.common.utility.configurationManager import ConfigurationManager
from dm.common.exceptions.configurationError import ConfigurationError
from dm.common.exceptions.internalError import InternalError
from dm.common.exceptions.objectNotFound import ObjectNotFound
from dm.common.exceptions.authenticationError import AuthenticationError
from dm.common.exceptions.communicationError import CommunicationError
from dm.common.exceptions.invalidArgument import InvalidArgument
from dm.common.exceptions.objectAlreadyExists import ObjectAlreadyExists
from dm.common.exceptions.dmException import DmException
from ldapClient import LdapClient
class LdapUserManager(LdapClient):
CONFIG_SECTION_NAME = 'LdapUserManager'
SERVER_URL_KEY = 'serverurl'
ADMIN_DN_KEY = 'admindn'
ADMIN_PASSWORD_FILE_KEY = 'adminpasswordfile'
USER_DN_FORMAT_KEY = 'userdnformat'
GROUP_DN_FORMAT_KEY = 'groupdnformat'
MIN_GID_NUMBER_KEY = 'mingidnumber'
def __init__(self, serverUrl, adminDn, adminPasswordFile, userDnFormat, groupDnFormat, minGidNumber=None):
LdapClient.__init__(self, serverUrl, adminDn, adminPasswordFile)
self.userDnFormat = userDnFormat
self.groupDnFormat = groupDnFormat
self.minGidNumber = minGidNumber
#self.getLogger().debug('Min GID number: %s' % minGidNumber)
# Remove first entry from the dn format to get tree base
self.groupBaseDn = ','.join(groupDnFormat.split(',')[1:])
#self.getLogger().debug('Group base DN: %s' % self.groupBaseDn)
@classmethod
def createInstance(cls):
cm = ConfigurationManager.getInstance()
logger = cls.getLogger()
logger.debug('Creating LDAP User Manager instance')
serverUrl = cm.getConfigOption(LdapUserManager.CONFIG_SECTION_NAME, LdapUserManager.SERVER_URL_KEY)
#logger.debug('Using server URL: %s' % serverUrl)
adminDn = cm.getConfigOption(LdapUserManager.CONFIG_SECTION_NAME, LdapUserManager.ADMIN_DN_KEY)
#logger.debug('Using admin DN: %s' % adminDn)
adminPasswordFile = cm.getConfigOption(LdapUserManager.CONFIG_SECTION_NAME, LdapUserManager.ADMIN_PASSWORD_FILE_KEY)
#logger.debug('Using admin password file: %s' % adminPasswordFile)
userDnFormat = cm.getConfigOption(LdapUserManager.CONFIG_SECTION_NAME, LdapUserManager.USER_DN_FORMAT_KEY)
#logger.debug('Using user DN format: %s' % userDnFormat)
groupDnFormat = cm.getConfigOption(LdapUserManager.CONFIG_SECTION_NAME, LdapUserManager.GROUP_DN_FORMAT_KEY)
#logger.debug('Using group DN format: %s' % groupDnFormat)
minGidNumber = cm.getConfigOption(LdapUserManager.CONFIG_SECTION_NAME, LdapUserManager.MIN_GID_NUMBER_KEY)
#logger.debug('Using min GID number: %s' % minGidNumber)
return LdapUserManager(serverUrl, adminDn, adminPasswordFile, userDnFormat, groupDnFormat, minGidNumber)
@classmethod
def decodePasswordHash(cls, b64EncodedString):
beginEncoding = b64EncodedString.find('}')
decodedString = b64EncodedString[beginEncoding+1:]
decodedString = b16encode(b64decode(decodedString)).upper()
return decodedString
@classmethod
def encodePasswordHash(cls, passwordHash):
encodedString = '{SHA}'+b64encode(b16decode(passwordHash))
return encodedString
@LdapClient.executeLdapCall
def getUserInfo(self, username):
""" Get user info. """
userDn = self.userDnFormat % str(username)
ldapClient = self.getLdapClient()
resultList = ldapClient.search_s(userDn, ldap.SCOPE_BASE, attrlist=['*','createTimeStamp','modifyTimestamp'])
userTuple = resultList[0]
return LdapUserInfo({'userDn' : userTuple[0], 'userAttrs' : userTuple[1]})
@LdapClient.executeLdapCall
def getUserInfos(self):
""" Get user infos. """
baseDnPos = self.userDnFormat.find(',')
baseDn = self.userDnFormat[baseDnPos+1:]
searchFilter = self.userDnFormat[0:baseDnPos] % '*'
ldapClient = self.getLdapClient()
resultList = ldapClient.search_s(baseDn, ldap.SCOPE_SUBTREE, searchFilter, attrlist=['*','createTimeStamp','modifyTimestamp'])
userList = []
for result in resultList:
userDn = result[0]
userAttrs = result[1]
userList.append(LdapUserInfo({'userDn' : userDn, 'userAttrs' : userAttrs}))
return userList
@LdapClient.executeLdapCall
def getUserInfoMapByUid(self):
""" Get user infos. """
baseDnPos = self.userDnFormat.find(',')
baseDn = self.userDnFormat[baseDnPos+1:]
searchFilter = self.userDnFormat[0:baseDnPos] % '*'
ldapClient = self.getLdapClient()
resultList = ldapClient.search_s(baseDn, ldap.SCOPE_SUBTREE, searchFilter)
userMap = {}
for result in resultList:
userDn = result[0]
userAttrs = result[1]
uid = userAttrs['uid'][0]
userMap[uid] = LdapUserInfo({'userDn' : userDn, 'userAttrs' : userAttrs})
return userMap
@LdapClient.executeLdapCall
def modifyUserInfo(self, username, attrDict):
""" Modify user. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
ldapUserInfo = self.getUserInfo(username)
userDn = ldapUserInfo.get('userDn')
userAttrs = ldapUserInfo.get('userAttrs')
# Remove internal LDAP attributes before creating copy for modifications
for key in [ 'modifyTimestamp', 'createTimeStamp' ]:
if userAttrs.has_key(key):
del userAttrs[key]
userAttrs2 = copy.copy(userAttrs)
middleName = attrDict.get('middleName', '')
fullName = '%s, %s' % (attrDict.get('lastName', ''), attrDict.get('firstName', ''))
if middleName:
fullName = '%s %s' % (fullName, middleName)
if attrDict.has_key('name'):
userAttrs2['cn'] = [attrDict.get('name')]
userAttrs2['revcn'] = ['%s %s' % (attrDict.get('lastName'), attrDict.get('firstName'))]
userAttrs2['sn'] = [attrDict.get('lastName')]
userAttrs2['givenName'] = [attrDict.get('firstName')]
userAttrs2['gecos'] = [attrDict.get('name')]
for key in ['loginShell', 'allowed-host', 'homeDirectory', 'home7Directory', 'gidNumber' ]:
if attrDict.has_key(key):
userAttrs2[key] = [attrDict.get(key)]
if attrDict.has_key('grp'):
userAttrs2['o'] = [attrDict.get('grp')]
if attrDict.has_key('inactive'):
if attrDict.get('inactive', 'N') == 'Y':
userAttrs2['inetUserStatus'] = ['inactive']
else:
userAttrs2['inetUserStatus'] = ['active']
passwordHash = attrDict.get('userPassword')
if not passwordHash:
passwordHash = attrDict.get('passwordHashValue')
if passwordHash:
userAttrs2['userPassword'] = [self.encodePasswordHash(passwordHash)]
#logger.debug('Encoded password entry: %s' % passwordHash)
logger.debug('Old user %s attrs: %s' % (username, userAttrs))
logger.debug('Modified user %s attrs: %s' % (username, userAttrs2))
userLdif = ldap.modlist.modifyModlist(userAttrs, userAttrs2)
ldapClient.modify_s(userDn, userLdif)
return LdapUserInfo({'userDn' : userDn, 'userAttrs' : userAttrs2})
@LdapClient.executeLdapCall
def createUserInfo(self, username, attrDict):
""" Create user. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
try:
ldapUserInfo = self.getUserInfo(username)
raise ObjectAlreadyExists('User %s already exists.' % username)
except ObjectNotFound, ex:
pass
middleName = attrDict.get('middleName')
fullName = '%s, %s' % (attrDict.get('lastName', ''), attrDict.get('firstName', ''))
if middleName:
fullName = '%s %s' % (fullName, middleName)
badgeNumber = attrDict.get('badgeNumber')
uid = 'd%s' % badgeNumber
if username != uid:
raise InvalidArgument('Username %s and badge number %s are not consistent.' % (username, badgeNumber))
userAttrs = {}
userAttrs['objectclass'] = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'inetUser', 'shadowAccount', 'posixAccount', 'passwordObject', 'APSexten', 'apple-user']
userAttrs['cn'] = [attrDict.get('name', fullName)]
userAttrs['revcn'] = ['%s %s' % (attrDict.get('lastName', ''), attrDict.get('firstName', ''))]
userAttrs['loginShell'] = ['/sbin/nologin']
userAttrs['allowed-host'] = ['dmid-vm']
userAttrs['uidNumber'] = ['10%s' % badgeNumber]
userAttrs['uid'] = [uid]
userAttrs['homeDirectory'] = ['/data']
userAttrs['home7Directory'] = ['/data']
userAttrs['o'] = [attrDict.get('grp')]
if attrDict.get('inactive', 'N') == 'Y':
userAttrs['inetUserStatus'] = ['inactive']
else:
userAttrs['inetUserStatus'] = ['active']
userAttrs['gidNumber'] = ['66001']
userAttrs['gecos'] = [attrDict.get('name', fullName)]
userAttrs['sn'] = [attrDict.get('lastName', '')]
userAttrs['givenName'] = [attrDict.get('firstName', '')]
passwordHash = attrDict.get('userPassword')
if not passwordHash:
passwordHash = attrDict.get('passwordHashValue')
if passwordHash:
userAttrs['userPassword'] = [self.encodePasswordHash(passwordHash)]
logger.debug('Creating user %s with attrs %s' % (username, userAttrs))
userDn = self.userDnFormat % str(username)
userLdif = ldap.modlist.addModlist(userAttrs)
ldapClient.add_s(userDn, userLdif)
return LdapUserInfo({'userDn' : userDn, 'userAttrs' : userAttrs})
def createGroup(self, name):
""" Create group if it does not exist. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
name = str(name)
try:
groupDn = self.groupDnFormat % name
logger.debug('Looking for group DN: %s' % groupDn)
# this method will throw exception if group is not found
resultList = ldapClient.search_s(groupDn, ldap.SCOPE_BASE)
groupTuple = resultList[0]
logger.debug('Group %s already exists' % groupTuple[0])
return
except ldap.NO_SUCH_OBJECT, ex:
logger.debug('Group DN %s must be created' % groupDn)
except Exception, ex:
raise InternalError(exception=ex)
# determine gidNumber: look through all entries to get max value,
# then increment it
# ldap should really be configured to handle gid's automatically,
# and should prevent invalid entries
try:
logger.debug('Looking for max group id')
resultList = ldapClient.search_s(self.groupBaseDn, ldap.SCOPE_ONELEVEL, attrlist=['gidNumber'])
maxGid = 0
if self.minGidNumber:
maxGid = self.minGidNumber
for result in resultList:
gidList = result[1].get('gidNumber', [])
gid = 0
if gidList:
gid = int(gidList[0])
if gid > maxGid:
maxGid = gid
gidNumber = str(maxGid + 1)
logger.debug('Max GID is %s, new group id will be %s' % (maxGid, gidNumber))
except Exception, ex:
raise InternalError(exception=ex)
attrs = {}
attrs['objectclass'] = ['posixGroup','top']
attrs['cn'] = name
attrs['gidNumber'] = [gidNumber]
attrs['memberUid'] = []
try:
groupLdif = ldap.modlist.addModlist(attrs)
ldapClient.add_s(groupDn, groupLdif)
except Exception, ex:
logger.error('Could not add group %s: %s' % (groupDn, ex))
raise InternalError(exception=ex)
def addUserToGroup(self, username, groupName):
""" Add user to group. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
username = str(username)
groupName = str(groupName)
try:
groupDn = self.groupDnFormat % groupName
resultList = ldapClient.search_s(groupDn, ldap.SCOPE_BASE)
groupTuple = resultList[0]
groupAttrs = groupTuple[1]
memberUidList = groupAttrs.get('memberUid', [])
if username in memberUidList:
logger.debug('Group %s already contains user %s' % (groupName, username))
return
except Exception, ex:
raise InternalError(exception=ex)
logger.debug('Adding user %s to group %s' % (username, groupName))
memberUidList2 = copy.copy(memberUidList)
memberUidList2.append(username)
groupAttrs2 = copy.copy(groupAttrs)
groupAttrs2['memberUid'] = memberUidList2
try:
groupLdif = ldap.modlist.modifyModlist(groupAttrs, groupAttrs2)
ldapClient.modify_s(groupDn, groupLdif)
except Exception, ex:
logger.error('Could not add user %s to group %s: %s' % (username, groupName, ex))
raise InternalError(exception=ex)
def deleteUserFromGroup(self, username, groupName):
""" Remove user from group. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
username = str(username)
groupName = str(groupName)
try:
groupDn = self.groupDnFormat % groupName
resultList = ldapClient.search_s(groupDn, ldap.SCOPE_BASE)
groupTuple = resultList[0]
groupAttrs = groupTuple[1]
memberUidList = groupAttrs.get('memberUid', [])
if username not in memberUidList:
logger.debug('Group %s does not contain user %s' % (groupName, username))
return
except Exception, ex:
raise InternalError(exception=ex)
logger.debug('Removing user %s from group %s' % (username, groupName))
memberUidList2 = copy.copy(memberUidList)
memberUidList2.remove(username)
groupAttrs2 = copy.copy(groupAttrs)
groupAttrs2['memberUid'] = memberUidList2
try:
groupLdif = ldap.modlist.modifyModlist(groupAttrs, groupAttrs2)
ldapClient.modify_s(groupDn, groupLdif)
except Exception, ex:
logger.error('Could not remove user %s from group %s: %s' % (username, groupName, ex))
raise InternalError(exception=ex)
def getGroupInfo(self, groupName):
""" Get given group info. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
groupName = str(groupName)
try:
groupDn = self.groupDnFormat % groupName
resultList = ldapClient.search_s(groupDn, ldap.SCOPE_BASE)
groupTuple = resultList[0]
groupAttrs = groupTuple[1]
return groupTuple
except Exception, ex:
raise InternalError(exception=ex)
def setGroupUsers(self, groupName, usernameList):
""" Set list of users for a given group. """
logger = self.getLogger()
ldapClient = self.getLdapClient()
groupName = str(groupName)
try:
groupDn = self.groupDnFormat % groupName
resultList = ldapClient.search_s(groupDn, ldap.SCOPE_BASE)
groupTuple = resultList[0]
groupAttrs = groupTuple[1]
except Exception, ex:
raise InternalError(exception=ex)
logger.debug('Setting users %s for group %s' % (usernameList, groupName))
memberUidList = []
for username in usernameList:
memberUidList.append(str(username))
groupAttrs2 = copy.copy(groupAttrs)
groupAttrs2['memberUid'] = memberUidList
try:
groupLdif = ldap.modlist.modifyModlist(groupAttrs, groupAttrs2)
ldapClient.modify_s(groupDn, groupLdif)
except Exception, ex:
logger.error('Could not set users %s for group %s: %s' % (usernameList, groupName, ex))
raise InternalError(exception=ex)
#######################################################################
# Testing.
if __name__ == '__main__':
utility = LdapUserManager('ldaps://dmid-vm.xray.aps.anl.gov:636', 'uid=dmadmin,ou=People,o=aps.anl.gov,dc=aps,dc=anl,dc=gov', '/tmp/ldapPassword', userDnFormat='uid=%s,ou=DM,ou=People,o=aps.anl.gov,dc=aps,dc=anl,dc=gov', groupDnFormat='cn=%s,ou=DM,ou=Group,o=aps.anl.gov,dc=aps,dc=anl,dc=gov', minGidNumber=66000)
#print utility.getGroupInfo(u's1id-test03')
user = utility.getUserInfo(u'd225159')
print user
print user.getUserPassword()
print utility.decodePasswordHash(user.getUserPassword())
userMap = utility.getUserInfoMapByUid()
print 'N entries: ', len(userMap)
print userMap['d225159']
#for user in userList:
# print user
utility.modifyUserInfo(u'd225159', {'homeDirectory' : '/data/'})
user = utility.getUserInfo(u'd225159')
print user
#user = utility.getUserInfo(u'd65114')
#print user
#passwordHash = LdapUserManager.decodePasswordHash(user['userAttrs']['userPassword'][0])
#print passwordHash
#print 'Creating user d000001'
#user = utility.getUserInfo(u'd000001')
#print user
#attrDict = {'badgeNumber' : '000001', 'name' : 'Test, User 1.', 'firstName' : 'User', 'middleName' : '1.', 'lastName' : 'Test', 'passwordHashValue' : '84673F4A8774846B1C096511C7F6B1329CEE5CCC', 'inactive' : 'N', 'grp' : 'XSD-TEST', 'lastChangeDate' : '2015-02-03 15:52:15', 'email' : 'UTEST1@APS.ANL.GOV', 'isUserNotAnlEmployee' : 'N' }
#try:
# user = utility.createUserInfo(u'd000001', attrDict)
#except ObjectAlreadyExists, ex:
# print 'User d000001 already exists, will modify it'
# user = utility.updateUserInfo(u'd000001', attrDict)
#print user
#print LdapUserManager.encodePasswordHash(passwordHash)
#utility.addLocalUserToGroup(u'sveseli', u'id8i-test02')
#print utility.getGroupInfo(u'id8i-test02')
#utility.deleteLocalUserFromGroup(u'sveseli', u'id8i-test02')
#print utility.getGroupInfo(u'id8i-test02')