From b9c45a69a8728fe7c39e1ecb7f7bfbb444e20cb0 Mon Sep 17 00:00:00 2001 From: Sinisa Veseli <sveseli@aps.anl.gov> Date: Thu, 13 Oct 2016 17:43:16 +0000 Subject: [PATCH] changes for ldap update functionality; added new command line tools for user account sync --- bin/dm-update-user-from-aps-db | 18 ++ bin/dm-update-users-from-aps-db | 18 ++ .../aps_user_db/cli/updateUserFromApsDbCli.py | 46 +++-- .../cli/updateUsersFromApsDbCli.py | 187 ++++++++++++++---- .../dm/aps_user_db/impl/apsUserDbManager.py | 54 ++--- src/python/dm/common/objects/apsUserInfo.py | 2 +- src/python/dm/common/objects/ldapUserInfo.py | 2 + .../dm/common/utility/ldapUserManager.py | 146 +++++++++++--- 8 files changed, 366 insertions(+), 107 deletions(-) create mode 100755 bin/dm-update-user-from-aps-db create mode 100755 bin/dm-update-users-from-aps-db diff --git a/bin/dm-update-user-from-aps-db b/bin/dm-update-user-from-aps-db new file mode 100755 index 00000000..415a875f --- /dev/null +++ b/bin/dm-update-user-from-aps-db @@ -0,0 +1,18 @@ +#!/bin/sh + +# Run command + +if [ -z $DM_ROOT_DIR ]; then + cd `dirname $0` && myDir=`pwd` + setupFile=$myDir/../setup.sh + if [ ! -f $setupFile ]; then + echo "Cannot find setup file: $setupFile" + exit 1 + fi + source $setupFile > /dev/null +fi +source dm_command_setup.sh + +eval "$DM_ROOT_DIR/src/python/dm/aps_user_db/cli/updateUserFromApsDbCli.py $DM_COMMAND_ARGS" + + diff --git a/bin/dm-update-users-from-aps-db b/bin/dm-update-users-from-aps-db new file mode 100755 index 00000000..8856b653 --- /dev/null +++ b/bin/dm-update-users-from-aps-db @@ -0,0 +1,18 @@ +#!/bin/sh + +# Run command + +if [ -z $DM_ROOT_DIR ]; then + cd `dirname $0` && myDir=`pwd` + setupFile=$myDir/../setup.sh + if [ ! -f $setupFile ]; then + echo "Cannot find setup file: $setupFile" + exit 1 + fi + source $setupFile > /dev/null +fi +source dm_command_setup.sh + +eval "$DM_ROOT_DIR/src/python/dm/aps_user_db/cli/updateUsersFromApsDbCli.py $DM_COMMAND_ARGS" + + diff --git a/src/python/dm/aps_user_db/cli/updateUserFromApsDbCli.py b/src/python/dm/aps_user_db/cli/updateUserFromApsDbCli.py index 130333f6..c23b4280 100755 --- a/src/python/dm/aps_user_db/cli/updateUserFromApsDbCli.py +++ b/src/python/dm/aps_user_db/cli/updateUserFromApsDbCli.py @@ -1,44 +1,62 @@ #!/usr/bin/env python +import os from dm.common.exceptions.invalidRequest import InvalidRequest +from dm.common.exceptions.objectNotFound import ObjectNotFound +from dm.common.exceptions.invalidArgument import InvalidArgument from dm.common.cli.dmCli import DmCli from dm.common.utility.ldapUserManager import LdapUserManager +from dm.common.utility.configurationManager import ConfigurationManager from dm.aps_user_db.api.apsUserDbApi import ApsUserDbApi class UpdateUserFromApsDbCli(DmCli): def __init__(self): DmCli.__init__(self) self.addOption('', '--badge', dest='badge', help='User badge number.') + self.addOption('', '--config-file', dest='configFile', help='Utility configuration file.') def checkArgs(self): if self.options.badge is None: raise InvalidRequest('Badge number must be provided.') + configFile = self.options.configFile + if configFile is None: + raise InvalidRequest('Config file must be provided.') + if not os.path.exists(configFile): + raise InvalidArgument('Invalid config file: %s.', configFile) def getBadge(self): return self.options.badge def runCommand(self): self.parseArgs(usage=""" - dm-update-user-from-aps-db --badge=BADGE + dm-update-user-from-aps-db --badge=BADGE --config-file=CONFIGFILE Description: Updates user in DM LDAP and database using information from APS DB. """) + ConfigurationManager.getInstance().setConfigFile(self.options.configFile) self.checkArgs() apsUserDbApi = ApsUserDbApi() - badgeNumber = self.getBadge() - username = 'd%s' % badgeNumber - apsUser = apsUserDbApi.getApsUserByBadgeNumber(badgeNumber) - print 'APS User Info: ', apsUser.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat()) - ldapUserManager = 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) - ldapUser = ldapUserManager.getUserInfo(username) - print 'Old LDAP User Info: ', ldapUser - attrDict = {} - attrDict['gecos'] = apsUser['name'] - attrDict['userPassword'] = apsUser['passwordHashValue'] - ldapUserManager.modifyUserInfo(username, attrDict) - ldapUser = ldapUserManager.getUserInfo(username) - print 'New LDAP User Info: ', ldapUser + badge = self.getBadge() + username = 'd%s' % badge + apsUser = apsUserDbApi.getApsUserByBadgeNumber(badge) + #print 'APS User Info: ', apsUser.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat()) + print 'APS User Info: ', apsUser + print + ldapUserManager = LdapUserManager.createInstance() + try: + self.logger.debug('Modifying LDAP user with badge %s: %s' % (badge, apsUser['name'])) + ldapUser = ldapUserManager.getUserInfo(username) + print 'LDAP User Info (before update): ', ldapUser + print 'LDAP User Password Hash (before update): ', ldapUserManager.decodePasswordHash(ldapUser.getUserPassword()) + print + ldapUserManager.modifyUserInfo(username, apsUser) + ldapUser = ldapUserManager.getUserInfo(username) + except ObjectNotFound, ex: + self.logger.debug('User with badge %s does not exist, creating it' % (badge)) + ldapUser = ldapUserManager.createUserInfo(username, apsUser) + print 'LDAP User Info (after update): ', ldapUser + print 'LDAP User Password Hash (after update): ', ldapUserManager.decodePasswordHash(ldapUser.getUserPassword()) ####################################################################### # Run command. diff --git a/src/python/dm/aps_user_db/cli/updateUsersFromApsDbCli.py b/src/python/dm/aps_user_db/cli/updateUsersFromApsDbCli.py index 8b13a527..8da0baef 100755 --- a/src/python/dm/aps_user_db/cli/updateUsersFromApsDbCli.py +++ b/src/python/dm/aps_user_db/cli/updateUsersFromApsDbCli.py @@ -1,8 +1,12 @@ #!/usr/bin/env python import datetime +import os from dm.common.exceptions.invalidRequest import InvalidRequest +from dm.common.exceptions.invalidArgument import InvalidArgument +from dm.common.exceptions.objectAlreadyExists import ObjectAlreadyExists from dm.common.cli.dmCli import DmCli +from dm.common.db.api.userDbApi import UserDbApi from dm.common.utility.configurationManager import ConfigurationManager from dm.common.utility.ldapUserManager import LdapUserManager from dm.aps_user_db.api.apsUserDbApi import ApsUserDbApi @@ -11,39 +15,83 @@ from dm.ds_web_service.api.dsRestApiFactory import DsRestApiFactory class UpdateUsersFromApsDbCli(DmCli): def __init__(self): DmCli.__init__(self) + self.addOption('', '--config-file', dest='configFile', help='Utility configuration file.') + self.addOption('', '--without-dm-db', dest='withoutDmDb', action='store_true', default=False, help='Do not use DM DB for synchronizing user information.') + self.addOption('', '--use-dm-rest-api', dest='useDmRestApi', action='store_true', default=False, help='Use DM REST API for accessing user information. This option does not have any effect if --without-dm-db is set.') + + def checkArgs(self): + configFile = self.options.configFile + if configFile is None: + raise InvalidRequest('Config file must be provided.') + if not os.path.exists(configFile): + raise InvalidArgument('Invalid config file: %s.', configFile) def runCommand(self): self.parseArgs(usage=""" - dm-update-users-from-aps-db + dm-update-users-from-aps-db --config-file=CONFIGFILE + [--without-dm-db] + [--use-dm-rest-api] Description: - Updates all users in DM LDAP and database using information from APS DB. + Updates all users in DM LDAP and database using information from APS DB. This command by default uses DM DB to determine whether or not user needs to be created or updated. If the --without-dm-db flag is set, DM DB will not be used nor it will be updated. """) - ConfigurationManager.getInstance().setConfigFile('/home/oxygen/SVESELI/Work/DM/etc/dm.conf') + self.checkArgs() + ConfigurationManager.getInstance().setConfigFile(self.options.configFile) self.logger.debug('Starting sync process') apsUserDbApi = ApsUserDbApi() - userRestApi = DsRestApiFactory.getUserRestApi() + ldapUserManager = LdapUserManager.createInstance() + + # Get DM user API + dmUserApi = None + if not self.options.withoutDmDb: + try: + if self.options.useDmRestApi: + self.logger.debug('Using DM REST API') + dmUserApi = DsRestApiFactory.getUserRestApi() + else: + self.logger.debug('Using DM DB API') + dmUserApi = UserDbApi() + except Exception, ex: + self.logger.error('Cannot use DM DB: %s' % ex) self.logger.debug('Retrieving APS users') apsUsers = apsUserDbApi.getApsUsers() self.logger.debug('Number of APS Users: %s' % len(apsUsers)) - self.logger.debug('Retrieving DM users') - dmUsers = userRestApi.getUsers() - self.logger.debug('Number of DM Users: %s' % len(dmUsers)) - dmUserBadgeMap = {} - for dmUser in dmUsers: - badge = dmUser.get('badge') - if badge: - dmUserBadgeMap[badge] = dmUser - self.logger.debug('Number of DM Users with badge: %s' % len(dmUserBadgeMap)) + + # Retrieve DM users from DB if we got DM user API + dmUserMap = {} + if dmUserApi: + self.logger.debug('Retrieving DM users from DB') + try: + nDmUsersWithBadge = 0 + dmUsers = dmUserApi.getUsers() + self.logger.debug('Number of DM Users: %s' % len(dmUsers)) + for dmUser in dmUsers: + username = dmUser.get('username') + if dmUser.get('badge'): + nDmUsersWithBadge += 1 + dmUserMap[username] = dmUser + self.logger.debug('Number of DM Users with badge: %s' % nDmUsersWithBadge) + except Exception, ex: + self.logger.error('Error retrieving users from DM DB: %s' % ex) + dmUserApi = None + + # We cannot use DM user API, retrieve users from LDAP + if not dmUserApi: + self.logger.debug('Retrieving DM users using LDAP client') + dmUserMap = ldapUserManager.getUserInfoMapByUid() + self.logger.debug('Number of DM LDAP Users: %s' % len(dmUserMap)) nCreatedUsers = 0 nUpdatedUsers = 0 + nErrors = 0 for apsUser in apsUsers: apsLastChangeDate = apsUser.get('lastChangeDate') + # DM DB attributes badge = apsUser['badgeNumber'] username = 'd%s' % badge + firstName = apsUser.get('firstName') middleName = apsUser.get('middleName') lastName = apsUser.get('lastName') @@ -54,43 +102,98 @@ Description: description = None password = None - dmUser = dmUserBadgeMap.get(badge) + # LDAP attributes + ldapModAttrDict = {} + ldapModAttrDict['gecos'] = apsUser['name'] + ldapModAttrDict['userPassword'] = apsUser['passwordHashValue'] + + dmUser = dmUserMap.get(username) if not dmUser: - self.logger.debug('Creating user with badge %s: %s' % (badge, apsUser['name'])) + # We must create user in LDAP/DM DB + try: + try: + self.logger.debug('Creating LDAP user with badge %s: %s' % (badge, apsUser['name'])) + ldapUser = ldapUserManager.createUserInfo(username, apsUser) + except ObjectAlreadyExists, ex: + # LDAP user already exists, simply update it. + self.logger.debug('LDAP user with badge %s already exists, attempting to modify it' % (badge)) + ldapUserManager.modifyUserInfo(username, ldapModAttrDict) + except Exception, ex: + self.logger.error('LDAP problem caught while creating user with badge %s: %s' % (badge, str(ex))) + nErrors += 1 + continue - userRestApi.addUser(username, firstName, lastName, middleName, email, badge, globusUsername, description, password, isLocalUser, lastUpdate) - nCreatedUsers += 1 + if dmUserApi: + try: + self.logger.debug('Creating DM DB user with badge %s: %s' % (badge, apsUser['name'])) + dmUserApi.addUser(username, firstName, lastName, middleName, email, badge, globusUsername, description, password, isLocalUser, lastUpdate) + nCreatedUsers += 1 + except Exception, ex: + self.logger.error('DM DB problem caught while creating user with badge %s: %s' % (badge, str(ex))) + nErrors += 1 + continue else: - dmLastUpdate = dmUser.get('lastUpdate') - if dmLastUpdate: - # need to convert DM last update time to datetime object - # datetime cannot handle time zone at the moment - timeZonePos = dmLastUpdate.rfind('-') - dmLastUpdate = dmLastUpdate[0:timeZonePos] - dmLastUpdate = datetime.datetime.strptime(dmLastUpdate, '%Y-%m-%d %H:%M:%S') - if not dmLastUpdate or dmLastUpdate < apsLastChangeDate: - id = dmUser.get('id') - self.logger.debug('Modifying user with badge %s: %s (DM id: %s)' % (badge, apsUser['name'], id)) - print apsUser - userRestApi.updateUser(id, username, firstName, lastName, middleName, email, badge, globusUsername, description, password, isLocalUser, lastUpdate) - nUpdatedUsers += 1 + # User already exists, simply update it. + + if dmUserApi: + # We have DM DB API + dmLastUpdate = str(dmUser.get('lastUpdate')) + if dmLastUpdate: + # need to convert DM last update time to datetime object + # datetime cannot handle time zone at the moment + timeZonePos = dmLastUpdate.rfind('-') + dmLastUpdate = dmLastUpdate[0:timeZonePos] + dmLastUpdate = datetime.datetime.strptime(dmLastUpdate, '%Y-%m-%d %H:%M:%S') + if not dmLastUpdate or dmLastUpdate < apsLastChangeDate: + # User needs update + try: + self.logger.debug('Modifying LDAP user with badge %s, username %s' % (badge, username)) + ldapUserManager.modifyUserInfo(username, ldapModAttrDict) + except Exception, ex: + self.logger.error('LDAP problem caught while modifying user with badge %s: %s' % (badge, str(ex))) + nErrors += 1 + continue + + try: + id = dmUser.get('id') + self.logger.debug('Modifying DM DB user with badge %s: %s (DM id: %s)' % (badge, apsUser['name'], id)) + dmUserApi.updateUser(id, username, firstName, lastName, middleName, email, badge, globusUsername, description, password, isLocalUser, lastUpdate) + nUpdatedUsers += 1 + except Exception, ex: + self.logger.error('DM DB problem caught while modifying user with badge %s: %s' % (badge, str(ex))) + nErrors += 1 + continue + else: + # User is up to date + continue else: - # User is up to date - pass - + # No DM DB API + apsPasswordHash = apsUser['passwordHashValue'] + dmPasswordHash = ldapUserManager.decodePasswordHash(dmUser['userAttrs']['userPassword'][0]) + if apsPasswordHash != dmPasswordHash: + # User needs update + try: + self.logger.debug('Modifying LDAP user with badge %s, username %s' % (badge, username)) + self.logger.debug('APS password hash: %s, DM password hash: %s' % (apsPasswordHash, dmPasswordHash)) + self.logger.debug('Encoded DM password hash: %s' % (dmUser['userAttrs']['userPassword'][0])) + ldapUserManager.modifyUserInfo(username, ldapModAttrDict) + nUpdatedUsers += 1 + except Exception, ex: + self.logger.error('LDAP problem caught while modifying user with badge %s: %s' % (badge, str(ex))) + nErrors += 1 + continue + else: + # User is up to date + continue + self.logger.debug('Number of new DM users: %s' % (nCreatedUsers)) self.logger.debug('Number of updated DM users: %s' % (nUpdatedUsers)) + self.logger.debug('Number of update errors: %s' % (nErrors)) self.logger.debug('Completed sync process') - #ldapUserManager = 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) - #ldapUser = ldapUserManager.getUserInfo(username) - #print 'Old LDAP User Info: ', ldapUser - #attrDict = {} - #attrDict['gecos'] = apsUser['name'] - #attrDict['userPassword'] = apsUser['passwordHashValue'] - #ldapUserManager.modifyUserInfo(username, attrDict) - #ldapUser = ldapUserManager.getUserInfo(username) - #print 'New LDAP User Info: ', ldapUser + print('Number of new DM users: %s' % (nCreatedUsers)) + print('Number of updated DM users: %s' % (nUpdatedUsers)) + print('Number of update errors: %s' % (nErrors)) ####################################################################### # Run command. diff --git a/src/python/dm/aps_user_db/impl/apsUserDbManager.py b/src/python/dm/aps_user_db/impl/apsUserDbManager.py index 1f7d1694..191319fa 100755 --- a/src/python/dm/aps_user_db/impl/apsUserDbManager.py +++ b/src/python/dm/aps_user_db/impl/apsUserDbManager.py @@ -24,7 +24,13 @@ class ApsUserDbManager: DB_CONNECTION_LOGGING_FLAG = False CONFIG_SECTION_NAME = 'ApsUserDbManager' - CONFIG_OPTION_NAME_LIST = [ 'dbHost', 'dbPort', 'dbUser', 'dbPasswordFile', 'dbName', 'dbSchema' ] + DB_KEY = 'db' + DB_USER_KEY = 'dbuser' + DB_HOST_KEY = 'dbhost' + DB_PORT_KEY = 'dbport' + DB_NAME_KEY = 'dbname' + DB_SCHEMA_KEY = 'dbschema' + DB_PASSWORD_FILE_KEY = 'dbpasswordfile' # Singleton. __lock = threading.RLock() @@ -39,6 +45,25 @@ class ApsUserDbManager: mgr = ex return mgr + def __configure(self): + cm = ConfigurationManager.getInstance() + self.logger.debug('Configuring APS User DB Manager') + self.db = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_KEY) + self.logger.debug('Using APS DB: %s' % self.db) + self.dbUser = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_USER_KEY) + self.logger.debug('Using APS DB user: %s' % self.dbUser) + self.dbHost = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_HOST_KEY) + self.logger.debug('Using APS DB host: %s' % self.dbHost) + self.dbPort = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_PORT_KEY) + self.logger.debug('Using APS DB port: %s' % self.dbPort) + self.dbName = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_NAME_KEY) + self.logger.debug('Using APS DB name: %s' % self.dbName) + self.dbSchema = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_SCHEMA_KEY) + self.logger.debug('Using APS DB schema: %s' % self.dbSchema) + self.dbPasswordFile = cm.getConfigOption(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.DB_PASSWORD_FILE_KEY) + self.logger.debug('Using APS DB password file: %s' % self.dbPasswordFile) + self.dbPassword = open(self.dbPasswordFile, 'r').readline().strip() + def __init__(self): ApsUserDbManager.__lock.acquire() try: @@ -47,34 +72,17 @@ class ApsUserDbManager: ApsUserDbManager.__instance = self self.lock = threading.RLock() self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__) - cm = ConfigurationManager.getInstance() - #cm.setOptionsFromConfigFile(ApsUserDbManager.CONFIG_SECTION_NAME, ApsUserDbManager.CONFIG_OPTION_NAME_LIST) - - #dbUser = cm.getDbUser() - #self.logger.debug('Using DB user: %s' % dbUser) - #dbSchema = cm.getDbSchema() - #dbPasswordFile = cm.getDbPasswordFile() - #self.logger.debug('Using DB password file: %s' % dbPasswordFile) - #dbPassword = open(dbPasswordFile, 'r').readline().strip() - #dbPort = cm.getDbPort() - #dbHost = cm.getDbHost() - #db = cm.getDb() - db = "oracle" - dbPort = 1527 - dbHost = "ra.aps.anl.gov" - dbName = "aps1" - dbSchema = "DCC" - dbUser = "glob_conn" - dbPassword = "ur2ytkownicy2u" - engineUrl = '%s://%s:%s@%s:%s/%s' % (db, dbUser, dbPassword, dbHost, dbPort, dbName) - self.logger.debug('Using engine URL: %s' % engineUrl) + self.__configure() + + engineUrl = '%s://%s:%s@%s:%s/%s' % (self.db, self.dbUser, self.dbPassword, self.dbHost, self.dbPort, self.dbName) + #self.logger.debug('Using engine URL: %s' % engineUrl) self.engine = sqlalchemy.create_engine(engineUrl, pool_size=ApsUserDbManager.DB_CONNECTION_POOL_SIZE, max_overflow=ApsUserDbManager.DB_CONNECTION_POOL_MAX_OVERFLOW, pool_recycle=ApsUserDbManager.DB_CONNECTION_POOL_RECYCYLE_TIME, echo=ApsUserDbManager.DB_CONNECTION_LOGGING_FLAG, pool_timeout=ApsUserDbManager.DB_CONNECTION_POOL_TIMEOUT) - self.metadata = sqlalchemy.MetaData(engineUrl, schema=dbSchema) + self.metadata = sqlalchemy.MetaData(engineUrl, schema=self.dbSchema) self.logger.debug('Mapping DB tables') for (dbTableName, (dbEntityClass, dbRelationDict)) in apsUserDbEntityMap.APS_USER_DB_ENTITY_MAP.items(): diff --git a/src/python/dm/common/objects/apsUserInfo.py b/src/python/dm/common/objects/apsUserInfo.py index 83228875..e5dbc2f8 100755 --- a/src/python/dm/common/objects/apsUserInfo.py +++ b/src/python/dm/common/objects/apsUserInfo.py @@ -4,7 +4,7 @@ from dmObject import DmObject class ApsUserInfo(DmObject): - DEFAULT_KEY_LIST = [ 'id', 'badgeNumber', 'firstName', 'middleName', 'lastName', 'email' ] + DEFAULT_KEY_LIST = [ 'id', 'badgeNumber', 'firstName', 'middleName', 'lastName', 'email', 'passwordHashValue' ] def __init__(self, dict): DmObject.__init__(self, dict) diff --git a/src/python/dm/common/objects/ldapUserInfo.py b/src/python/dm/common/objects/ldapUserInfo.py index e9be08a8..de890773 100755 --- a/src/python/dm/common/objects/ldapUserInfo.py +++ b/src/python/dm/common/objects/ldapUserInfo.py @@ -9,3 +9,5 @@ class LdapUserInfo(DmObject): def __init__(self, dict): DmObject.__init__(self, dict) + def getUserPassword(self): + return self['userAttrs']['userPassword'][0] diff --git a/src/python/dm/common/utility/ldapUserManager.py b/src/python/dm/common/utility/ldapUserManager.py index 8f1e53aa..bbaa5246 100755 --- a/src/python/dm/common/utility/ldapUserManager.py +++ b/src/python/dm/common/utility/ldapUserManager.py @@ -25,6 +25,14 @@ 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 @@ -35,9 +43,29 @@ class LdapUserManager(LdapClient): 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): - decodedString = b64EncodedString.replace('{SHA}','') + beginEncoding = b64EncodedString.find('}') + decodedString = b64EncodedString[beginEncoding+1:] decodedString = b16encode(b64decode(decodedString)).upper() return decodedString @@ -51,10 +79,41 @@ class LdapUserManager(LdapClient): """ Get user info. """ userDn = self.userDnFormat % str(username) ldapClient = self.getLdapClient() - resultList = ldapClient.search_s(userDn, ldap.SCOPE_BASE) + 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. """ @@ -64,21 +123,36 @@ class LdapUserManager(LdapClient): userDn = ldapUserInfo.get('userDn') userAttrs = ldapUserInfo.get('userAttrs') userAttrs2 = copy.copy(userAttrs) - for name,value in attrDict.items(): - ldapName = name - ldapValue = value - if name == 'passwordHashValue': - ldapName = 'userPassword' - ldapValue = self.encodePasswordHash(value) - if not userAttrs2.has_key(ldapName): - raise InvalidArgument('No such attribute: %s' % ldapName) - - if type(ldapValue) == types.ListType: - userAttrs2[ldapName] = ldapValue + + 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[ldapName] = [str(ldapValue)] + 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('Modifying user %s with attrs %s' % (username, userAttrs2)) - logger.debug('Modifying user %s attrs %s' % (username, userAttrs2)) userLdif = ldap.modlist.modifyModlist(userAttrs, userAttrs2) ldapClient.modify_s(userDn, userLdif) return LdapUserInfo({'userDn' : userDn, 'userAttrs' : userAttrs2}) @@ -93,9 +167,9 @@ class LdapUserManager(LdapClient): raise ObjectAlreadyExists('User %s already exists.' % username) except ObjectNotFound, ex: pass - middleName = attrDict.get('middleName', '') + middleName = attrDict.get('middleName') fullName = '%s, %s' % (attrDict.get('lastName', ''), attrDict.get('firstName', '')) - if len(middleName): + if middleName: fullName = '%s %s' % (fullName, middleName) badgeNumber = attrDict.get('badgeNumber') uid = 'd%s' % badgeNumber @@ -113,7 +187,10 @@ class LdapUserManager(LdapClient): userAttrs['homeDirectory'] = ['/data'] userAttrs['home7Directory'] = ['/data'] userAttrs['o'] = [attrDict.get('grp')] - userAttrs['inetUserStatus'] = ['active'] + 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', '')] @@ -285,22 +362,37 @@ class LdapUserManager(LdapClient): 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') + #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 + #user = utility.getUserInfo(u'd65114') + #print user - passwordHash = LdapUserManager.decodePasswordHash(user['userAttrs']['userPassword'][0]) - print passwordHash + #passwordHash = LdapUserManager.decodePasswordHash(user['userAttrs']['userPassword'][0]) + #print passwordHash - 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' } - user = utility.createUserInfo(u'd000001', attrDict) - user = utility.getUserInfo(u'd000001') - print user + #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) -- GitLab