From 4c613528cf401a2aefb5f3e9b3091decca5b7f5c Mon Sep 17 00:00:00 2001 From: Sinisa Veseli <sveseli@aps.anl.gov> Date: Thu, 17 Sep 2015 01:44:54 +0000 Subject: [PATCH] adding ldap platform utility for group management --- .../utility/ldapLinuxPlatformUtility.py | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100755 src/python/dm/common/utility/ldapLinuxPlatformUtility.py diff --git a/src/python/dm/common/utility/ldapLinuxPlatformUtility.py b/src/python/dm/common/utility/ldapLinuxPlatformUtility.py new file mode 100755 index 00000000..74f1d17e --- /dev/null +++ b/src/python/dm/common/utility/ldapLinuxPlatformUtility.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +import ldap +import ldap.modlist +import copy + +from dm.common.utility.loggingManager import LoggingManager +from dm.common.exceptions.configurationError import ConfigurationError +from dm.common.exceptions.internalError import InternalError +from dm.common.exceptions.authenticationError import AuthenticationError +from dm.common.exceptions.communicationError import CommunicationError +from dmSubprocess import DmSubprocess + +class LdapLinuxPlatformUtility: + + SUDO_CMD = '/usr/bin/sudo' + GROUPADD_CMD = '/usr/sbin/groupadd' + USERMOD_CMD = '/usr/sbin/usermod' + SETFACL_CMD = '/usr/bin/setfacl' + CHOWN_CMD = '/bin/chown' + + def __init__(self, serverUrl, adminDn, adminPasswordFile, groupDnFormat, minGidNumber=None): + self.serverUrl = serverUrl + self.adminDn = adminDn + 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) + self.getLogger().debug('Using LDAP Admin password file: %s' % adminPasswordFile) + self.adminPassword = open(adminPasswordFile, 'r').readline().strip() + if not self.adminPassword: + raise ConfigurationError('LDAP password could not be found in %s file' % adminPasswordFile) + self.ldapClient = None + + @classmethod + def getLogger(cls): + logger = LoggingManager.getInstance().getLogger(cls.__name__) + return logger + + @classmethod + def executeSudoCommand(cls, cmd): + p = DmSubprocess('%s %s' % (cls.SUDO_CMD, cmd)) + p.run() + + def getLdapClient(self): + if self.ldapClient is not None: + try: + self.ldapClient.simple_bind_s(self.adminDn, self.adminPassword) + except Exception, ex: + self.getLogger().error('Invalidating LDAP client due to error: %s' % ex) + self.unbind(self.ldapClient) + self.ldapClient = None + + if not self.ldapClient: + self.ldapClient = self.bind(self.serverUrl, self.adminDn, self.adminPassword) + return self.ldapClient + + @classmethod + def bind(cls, serverUrl, adminDn, adminPassword): + try: + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) + ldapClient = ldap.initialize(serverUrl) + ldapClient.set_option(ldap.OPT_REFERRALS,0) + ldapClient.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) + ldapClient.simple_bind_s(adminDn, adminPassword) + cls.getLogger().debug('Successful binding with LDAP DN: %s' % adminDn) + return ldapClient + except ldap.INVALID_CREDENTIALS, ex: + ldapClient.unbind() + raise AuthenticationError('Invalid LDAP credentials for admin user %s' % adminDn) + except ldap.SERVER_DOWN, ex: + raise CommunicationError('Cannot reach LDAP server %s' % serverUrl) + except Exception, ex: + raise InternalError('Unrecognized exception while binding to LDAP server %s: %s' % (serverUrl, ex)) + + @classmethod + def unbind(cls, ldapClient): + try: + ldapClient.unbind() + except Exception, ex: + cls.getLogger().error('Could not unbind LDAP client: %s' % ex) + + def createGroup(self, name): + """ Create group if it does not exist. """ + logger = self.getLogger() + ldapClient = self.getLdapClient() + 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() + 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) + + @classmethod + def setPathReadExecutePermissionsForGroup(cls, path, groupName): + """ Set path permissions for the given group. """ + logger = cls.getLogger() + logger.debug('Allowing group %s to read/execute path %s' % (groupName, path)) + cmd = '%s -m group\:%s\:rx %s' % (cls.SETFACL_CMD, groupName, path) + cls.executeSudoCommand(cmd) + + @classmethod + def changePathGroupOwner(cls, path, groupName): + logger = cls.getLogger() + logger.debug('Changing group owner to %s for path %s' % (groupName, path)) + cmd = '%s -R \:%s %s' % (cls.CHOWN_CMD, groupName, path) + cls.executeSudoCommand(cmd) + +####################################################################### +# Testing. + +if __name__ == '__main__': + utility = LdapLinuxPlatformUtility('ldaps://dmid-vm.xray.aps.anl.gov:636', 'uid=dmadmin,ou=People,o=aps.anl.gov,dc=aps,dc=anl,dc=gov', '/tmp/ldapPassword', groupDnFormat='cn=%s,ou=DM,ou=Group,o=aps.anl.gov,dc=aps,dc=anl,dc=gov', minGidNumber=66000) + utility.createGroup('exp2') + #utility.createGroup('dmgp000001_TOF') + utility.addUserToGroup('rs1', 'exp2') + -- GitLab