def get_schema(self, ldap_obj_class_name_or_oid, reload=False, do_reload=False): #pylint: disable=redefined-builtin """Get attributes as provided by schema - we only do this once when it is requested - we cache the result as this should not change in a single run - unless the reload flag is set :-D @type ldap_obj_class_name_or_oid: LDAP class name or OID to get the schema from (passed to ldap.schema.subentry.SubSchema) @type reload: deprecated argument, do not use anymore, use do_reload @type do_reload: boolean indicating if the schema should be returned form the cache or reloaded from the LDAP server. @returns: dictionary containing details about the schema there is an entry for each attribute with the following keys - ['oid'] gives the corresponding oid for the attribute - ['single'] gives True for a single-value attribute, False otherwise @raise: LDAPError when the schema cannot be fetched for the given object class """ if reload: logging.warning( 'usage of reload is deprecated, use do_reload instead') do_reload = reload if self.ldap is None: self.bind() if not do_reload and ldap_obj_class_name_or_oid in self.schema: return self.schema[ldap_obj_class_name_or_oid] self.schema[ldap_obj_class_name_or_oid] = {} try: # returns ('cn=Subschema', <ldap.schema.subentry.SubSchema instance at 0x1986878>) schematype, schema = ldap.schema.subentry.urlfetch( self.ldap.ldap_url.unparse()) except ldap.LDAPError: self.log.raiseException("Failed to fetch schema") attributes = {} if schematype == 'cn=Subschema': try: # this returns a list of dicts for x in schema.attribute_types([ldap_obj_class_name_or_oid]): attributes.update(x) except Exception: self.log.raiseException( "Failed to retrieve attributes from schematype %s and ldap_obj_class_name_or_oid %s" % (schematype, ldap_obj_class_name_or_oid)) else: self.log.error('Unknown returned schematype %s' % schematype) if len(attributes) == 0: self.log.error( "No attributes from schematype %s and ldap_obj_class_name_or_oid %s" % (schematype, ldap_obj_class_name_or_oid)) return None for attr in attributes.values(): oid = attr.oid single = attr.single_value == 1 name = attr.names[0] if len(attr.names) > 1: # what with multiple names? self.log.error( "Multiple names associated with attr, only using first one. From %s: oid %s names %s" % (ldap_obj_class_name_or_oid, oid, attr.names)) self.schema[ldap_obj_class_name_or_oid][name] = {} self.schema[ldap_obj_class_name_or_oid][name]['single'] = single self.schema[ldap_obj_class_name_or_oid][name]['oid'] = oid return self.schema[ldap_obj_class_name_or_oid]
def get_schema(self, ldap_obj_class_name_or_oid, reload=False): """Get attributes as provided by schema - we only do this once when it is requested - we cache the result as this should not change in a single run - unless the reload flag is set :-D @type ldap_obj_class_name_or_oid: LDAP class name or OID to get the schema from (passed to ldap.schema.subentry.SubSchema) @type reload: boolean indicating if the schema should be returned form the cache or reloaded from the LDAP server. @returns: dictionary containing details about the schema there is an entry for each attribute with the following keys - ['oid'] gives the corresponding oid for the attribute - ['single'] gives True for a single-value attribute, False otherwise @raise: LDAPError when the schema cannot be fetched for the given object class """ if self.ldap is None: self.bind() if not reload and ldap_obj_class_name_or_oid in self.schema: return self.schema[ldap_obj_class_name_or_oid] self.schema[ldap_obj_class_name_or_oid] = {} try: # returns ('cn=Subschema', <ldap.schema.subentry.SubSchema instance at 0x1986878>) schematype, schema = ldap.schema.subentry.urlfetch(self.ldap.ldap_url.unparse()) except ldap.LDAPError: self.log.raiseException("Failed to fetch schema from url %s" % (self.ldap.ldap_url)) attributes = {} if schematype == 'cn=Subschema': try: # this returns a list of dicts for x in schema.attribute_types([ldap_obj_class_name_or_oid]): attributes.update(x) except Exception: self.log.raiseException("Failed to retrieve attributes from schematype %s and ldap_obj_class_name_or_oid %s" % (schematype, ldap_obj_class_name_or_oid)) else: self.log.error('Unknown returned schematype %s' % schematype) if len(attributes) == 0: self.log.error("No attributes from schematype %s and ldap_obj_class_name_or_oid %s" % (schematype, ldap_obj_class_name_or_oid)) return None for attr in attributes.values(): oid = attr.oid single = attr.single_value == 1 name = attr.names[0] if len(attr.names) > 1: # what with multiple names? self.log.error("Multiple names associated with attr, only using first one. From %s: oid %s names %s" % (ldap_obj_class_name_or_oid, oid, attr.names)) self.schema[ldap_obj_class_name_or_oid][name] = {} self.schema[ldap_obj_class_name_or_oid][name]['single'] = single self.schema[ldap_obj_class_name_or_oid][name]['oid'] = oid return self.schema[ldap_obj_class_name_or_oid]
print(attr_type,str(se_orig)) print('*** Testing object class inetOrgPerson ***') drink = schema.get_obj(ldap.schema.AttributeType,'favouriteDrink') if not drink is None: print('*** drink ***') print('drink.names',repr(drink.names)) print('drink.collective',repr(drink.collective)) inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass,'inetOrgPerson') if not inetOrgPerson is None: print(inetOrgPerson.must,inetOrgPerson.may) print('*** person,organizationalPerson,inetOrgPerson ***') try: print(schema.attribute_types() ['person','organizationalPerson','inetOrgPerson'] ) print(schema.attribute_types() ['person','organizationalPerson','inetOrgPerson'], attr_type_filter = [ ('no_user_mod',[0]), ('usage',range(2)), ] ) except KeyError as e: print('***KeyError',str(e)) schema.ldap_entry() print(str(schema.get_obj(ldap.schema.MatchingRule,'2.5.13.0')))
for attr_type, schema_class in ldap.schema.SCHEMA_CLASS_MAPPING.items(): print "*" * 20, attr_type, "*" * 20 for element_id in schema.listall(schema_class): se_orig = schema.get_obj(schema_class, element_id) se_reverse = schema_reverse.get_obj(schema_class, element_id) # assert str(se_orig)==str(se_reverse) print attr_type, str(se_orig) print "*** Testing object class inetOrgPerson ***" inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass, "inetOrgPerson") if not inetOrgPerson is None: print inetOrgPerson.must, inetOrgPerson.may print "*** person,organizationalPerson,inetOrgPerson ***" try: print schema.attribute_types(["person", "organizationalPerson", "inetOrgPerson"]) print schema.attribute_types( ["person", "organizationalPerson", "inetOrgPerson"], attr_type_filter=[("no_user_mod", [0]), ("usage", range(2))], ) except KeyError, e: print "***KeyError", str(e) drink = schema.get_obj(ldap.schema.AttributeType, "favouriteDrink") if not drink is None: print "*** drink ***" print "drink.names", repr(drink.names) print "drink.collective", repr(drink.collective) schema.ldap_entry()
print(attr_type, str(se_orig)) print('*** Testing object class inetOrgPerson ***') drink = schema.get_obj(ldap.schema.AttributeType, 'favouriteDrink') if not drink is None: print('*** drink ***') print('drink.names', repr(drink.names)) print('drink.collective', repr(drink.collective)) inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass, 'inetOrgPerson') if not inetOrgPerson is None: print(inetOrgPerson.must, inetOrgPerson.may) print('*** person,organizationalPerson,inetOrgPerson ***') try: print(schema.attribute_types()['person', 'organizationalPerson', 'inetOrgPerson']) print(schema.attribute_types()['person', 'organizationalPerson', 'inetOrgPerson'], attr_type_filter=[ ('no_user_mod', [0]), ('usage', range(2)), ]) except KeyError as e: print('***KeyError', str(e)) schema.ldap_entry() print(str(schema.get_obj(ldap.schema.MatchingRule, '2.5.13.0'))) print(str(schema.get_obj(ldap.schema.MatchingRuleUse, '2.5.13.0'))) print(str(schema.get_obj(ldap.schema.AttributeType, 'name')))
print('*** Testing object class inetOrgPerson ***') drink = schema.get_obj(ldap.schema.AttributeType, 'favouriteDrink') if not drink is None: print('*** drink ***') print('drink.names', repr(drink.names)) print('drink.collective', repr(drink.collective)) inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass, 'inetOrgPerson') if not inetOrgPerson is None: print(inetOrgPerson.must, inetOrgPerson.may) print('*** person,organizationalPerson,inetOrgPerson ***') try: print( schema.attribute_types( ['person', 'organizationalPerson', 'inetOrgPerson'])) print( schema.attribute_types( ['person', 'organizationalPerson', 'inetOrgPerson'], attr_type_filter=[ ('no_user_mod', [0]), ('usage', range(2)), ])) except KeyError as e: print('***KeyError', str(e)) schema.ldap_entry() print(str(schema.get_obj(ldap.schema.MatchingRule, '2.5.13.0'))) print(str(schema.get_obj(ldap.schema.MatchingRuleUse, '2.5.13.0')))
print('*'*20,attr_type,'*'*20) for element_id in schema.listall(schema_class): se_orig = schema.get_obj(schema_class,element_id) se_reverse = schema_reverse.get_obj(schema_class,element_id) # assert str(se_orig)==str(se_reverse) print(attr_type,str(se_orig)) print('*** Testing object class inetOrgPerson ***') inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass,'inetOrgPerson') if not inetOrgPerson is None: print(inetOrgPerson.must,inetOrgPerson.may) print('*** person,organizationalPerson,inetOrgPerson ***') try: print(schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'] )) print(schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'], attr_type_filter = [ ('no_user_mod',[0]), ('usage',list(range(2))), ] )) except KeyError as e: print('***KeyError',str(e)) drink = schema.get_obj(ldap.schema.AttributeType,'favouriteDrink') if not drink is None: print('*** drink ***') print('drink.names',repr(drink.names))
import sys, pprint, ldap, ldap.schema schema_attrs = ldap.schema.SCHEMA_ATTRS ldap.set_option(ldap.OPT_DEBUG_LEVEL, 0) ldap._trace_level = 0 subschemasubentry_dn, schema = ldap.schema.urlfetch(sys.argv[-1]) if subschemasubentry_dn is None: print 'No sub schema sub entry found!' sys.exit(1) oc_list = [ 'person', 'organizationalPerson', 'inetOrgPerson', ] struct_oc = schema.get_structural_oc(oc_list) #print str(schema.get_obj(ldap.schema.ObjectClass,struct_oc)) pprint.pprint(schema.get_applicable_aux_classes(struct_oc)) pprint.pprint(schema.attribute_types(oc_list))
print '*'*20,attr_type,'*'*20 for element_id in schema.listall(schema_class): se_orig = schema.get_obj(schema_class,element_id) se_reverse = schema_reverse.get_obj(schema_class,element_id) # assert str(se_orig)==str(se_reverse) print attr_type,str(se_orig) print '*** Testing object class inetOrgPerson ***' inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass,'inetOrgPerson') if not inetOrgPerson is None: print inetOrgPerson.must,inetOrgPerson.may print '*** person,organizationalPerson,inetOrgPerson ***' try: print schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'] ) print schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'], attr_type_filter = [ ('no_user_mod',[0]), ('usage',range(2)), ] ) except KeyError,e: print '***KeyError',str(e) drink = schema.get_obj(ldap.schema.AttributeType,'favouriteDrink') if not drink is None: print '*** drink ***' print 'drink.names',repr(drink.names)
print '*** Testing object class inetOrgPerson ***' drink = schema.get_obj(ldap.schema.AttributeType,'favouriteDrink') if not drink is None: print '*** drink ***' print 'drink.names',repr(drink.names) print 'drink.collective',repr(drink.collective) inetOrgPerson = schema.get_obj(ldap.schema.ObjectClass,'inetOrgPerson') if not inetOrgPerson is None: print inetOrgPerson.must,inetOrgPerson.may print '*** person,organizationalPerson,inetOrgPerson ***' try: print schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'] ) print schema.attribute_types( ['person','organizationalPerson','inetOrgPerson'], attr_type_filter = [ ('no_user_mod',[0]), ('usage',range(2)), ] ) except KeyError,e: print '***KeyError',str(e) schema.ldap_entry() print str(schema.get_obj(ldap.schema.MatchingRule,'2.5.13.0'))
class LdapQuery(object): """Singleton class to interact with the LDAP server. This level is LDAP-schema aware. It knows about the dn for people, groups and projects. Allows searching for - users - groups - projects If distinct group types that are required that are identified, e.g., by specific names, subclass LdapQuery and/or use the post_filter arguments. """ __metaclass__ = Singleton def __init__(self, configuration): """ Initalisation. @type configuration: a SchemaConfiguration instance. If you initialise using None as the configuration, this will not fail if the singleton has already been created. """ self.log = fancylogger.getLogger(name=self.__class__.__name__) self.configuration = configuration self.ldap = LdapConnection(configuration) self.ldap.connect() self.ldap.bind() self.schema = {} def _filter_search(self, base, multi_value_attributes, ldap_filter, attributes, post_filter): entries = self.ldap.search(ldap_filter, base, attributes) results = [self.__delist_ldap_return_value(e, multi_value_attributes, attributes) for e in entries] if post_filter: (attribute, regex) = post_filter results = [r for r in results if regex.match(r[attribute])] self.log.debug("_filter_search finds %d results" % (len(results))) return results def group_filter_search(self, ldap_filter, attributes=None, post_filter=None): """Perform an LDAP lookup in the group tree, based on the given filter. @type ldap_filter: string describing an LDAP filter or LdapFilter instance @type attributes: list of strings describing LDAP attributes. If this is None (default), we return all retrieved attributes. @type post_filter: (attribute name, compiled regex), allows filtering the results based on the specified attribute matching the givenregex. Note that the attribute is matched, not searched. @returns: list of matching LDAP entries as dictionaries, limited to the requested attributes. @raise ldap.OTHER if the LDAP connection was not properly instantiated """ if not self.ldap: self.log.error("LDAP search request (group_filter_search) failed: ldap not initialised") raise ldap.OTHER() # for groups, we use the following base self.log.info("group_filter_search: ldap_filter = %s, requested attributes = %s" % (ldap_filter, attributes)) return self._filter_search(self.configuration.group_dn_base, self.configuration.group_multi_value_attributes, ldap_filter, attributes, post_filter) def group_search(self, cn, member_uid, attributes=None): """Perform an LDAP lookup in the group tree, looking for the entry with given cn and memberUid. @type cn: string representing the desired common name in the LDAP database @type member_uid: string representing the member's user id in the LDAP database? @type attributes: list of strings describing LDAP attributes. If this is None (default), we return all the retrieved attributes @returns: the matching LDAP entry as a dictionary, limited to the requested attributes. """ self.log.info("group_search: cn = %s, member_uid = %s, requested attributes = %s" % (cn, member_uid, attributes)) cn_filter = CnFilter(cn) member_filter = MemberFilter(member_uid) result = self.group_filter_search(cn_filter & member_filter, attributes) self.log.debug("group_search for %s, %s yields %s" % (cn, member_uid, result)) if not result is None and len(result) > 0: return result[0] else: self.log.debug("group_search returning None") return None def project_filter_search(self, ldap_filter, attributes=None, post_filter=None): """Perform an LDAP lookup in the projects tree, based on the given filter. @type ldap_filter: string describing an LDAP filter or LdapFilter instance @type attributes: list of strings describing LDAP attributes. If this is None (default), we return all retrieved attributes @type post_filter: (attribute name, compiled regex), allows filtering the results based on the specified attribute matching the givenregex. Note that the attribute is matched, not searched. @returns: list of matching LDAP entries as dictionaries, limited to the requested attributes. @raise ldap.OTHER if the LDAP connection was not properly instantiated """ if not self.ldap: self.log.error("LDAP search request (user_filter_search) failed: ldap not initialised") raise ldap.OTHER() return self._filter_search(self.configuration.project_dn_base, self.configuration.project_multi_value_attributes, ldap_filter, attributes, post_filter) def vo_filter_search(self, ldap_filter, attributes=None, post_filter=None): """Perform an LDAP lookup in the VO tree, based on the given filter. @type ldap_filter: string describing an LDAP filter or LdapFilter instance @type attributes: list of strings describing LDAP attributes. If this is None (default), we return all retrieved attributes @type post_filter: (attribute name, compiled regex), allows filtering the results based on the specified attribute matching the givenregex. Note that the attribute is matched, not searched. @returns: list of matching LDAP entries as dictionaries, limited to the requested attributes. @raise ldap.OTHER if the LDAP connection was not properly instantiated """ if not self.ldap: self.log.error("LDAP search request (user_filter_search) failed: ldap not initialised") raise ldap.OTHER() return self._filter_search(self.configuration.vo_dn_base, self.configuration.vo_multi_value_attributes, ldap_filter, attributes, post_filter) def user_filter_search(self, ldap_filter, attributes=None, post_filter=None): """Perform an LDAP lookup in the user tree, based on the given filter. @type filter: string describing an LDAP filter or LdapFilter instance @type attributes: list of strings describing LDAP attributes. If this is None (default), we return all retrieved attributes @type post_filter: (attribute name, compiled regex), allows filtering the results based on the specified attribute matching the givenregex. Note that the attribute is matched, not searched. @returns: list of matching LDAP entries as dictionaries, limited to the requested attributes. @raise ldap.OTHER if the LDAP connection was not properly instantiated """ if not self.ldap: self.log.error("LDAP search request (user_filter_search) failed: ldap not initialised") raise ldap.OTHER() # For users, we use the following base: self.log.info("user_filter_search: filter = %s, requested attributes = %s" % (filter, attributes)) return self._filter_search(self.configuration.user_dn_base, self.configuration.user_multi_value_attributes, ldap_filter, attributes, post_filter) def user_search(self, user_id, institute, attributes=None): """Perform an LDAP search for the given user and institute. Note that both user_id and institute are mandatory here. If this is not what you want, you should instead use user_filter_search. @type user_id: string representing the user login in the given institute @type institute: string representing the institute @type attributes: list of string describing LDAP attributes. If this is None (default) then all attributes are returned. @returns: a dictionary, with the values for the requested attributes for the given user """ login_filter = LdapFilter("instituteLogin=%s" % (user_id)) institute_filter = LdapFilter("institute=%s" % (institute)) result = self.user_filter_search(login_filter & institute_filter, attributes) self.log.debug("user_search for %s, %s yields %s" % (user_id, institute, result)) if not result is None and len(result) > 0: return result[0] else: self.log.debug("user_search returning None") return None def __delist_ldap_return_value(self, entry, list_attributes=None, attributes=None): """Get sensible values, i.e., not lists in the LDAP returned values if they're not needed. Note that a user can have e.g., multiple public keys, so we're keeping that as a list. Such attributes should be specified in the list_attributes @type entry: LDAP result entry as a dictionary @type list_attributes @type attributes: attribute names we wish to retain """ kvs = [(k, v[0]) for (k, v) in entry.iteritems() if (list_attributes is None or k not in list_attributes) and (attributes is None or k in attributes)] # we do want to get all the attributes that provide multiple items for a in list_attributes: if (attributes is None or a in attributes) and a in entry: kvs.append((a, entry[a])) return dict(kvs) def __modify(self, current, dn, attributes): """Actually make the modification.""" current_ = {} for key in attributes.keys(): current_[key] = current.get(key, []) # [(ldap.MOD_REPLACE, k, v) for (k,v) in attributes.iteritems()] modification_attributes = ldap.modlist.modifyModlist(current_, attributes) self.ldap.modify_attributes(dn, modification_attributes) def group_modify(self, cn, attributes): """Change one or more attributes for a given group. @type cn: string representing the common name for the group @type attributes: dictionary with the attribute names and their new values @raise: NoSuchGroupError """ dn = "cn=%s,%s" % (cn, self.configuration.group_dn_base) current = self.group_filter_search(CnFilter(cn)) if current is None: self.log.error("group_modify did not find group with cn = %s (dn = %s)" % (cn, dn)) raise NoSuchGroupError(cn) self.log.debug("group_modify current attribute values = %s - new attribute values = %s" % (current[0], attributes)) self.__modify(current[0], dn, attributes) def user_modify(self, cn, attributes): """Change one or more attributes for a given user. @type cn: string representing the common name for the user @type attributes: dictionary with the attribute names and their new values @raise: NoSuchUserError """ dn = "cn=%s,%s" % (cn, self.configuration.user_dn_base) current = self.user_filter_search(CnFilter(cn)) if current is None: self.log.error("user_modify did not find user with cn = %s (dn = %s)" % (cn, dn)) raise NoSuchUserError(cn) self.log.debug("user_modify current attribute values = %s - new attribute values = %s" % (current[0], attributes)) self.__modify(current[0], dn, attributes) def project_modify(self, cn, attributes): """Change one or more attributes for a given project. @type cn: string representing the common name for the user @type attributes: dictionary with the attribute names and their new values @raise: NoSuchProjectError """ dn = "cn=%s,%s" % (cn, self.configuration.project_dn_base) current = self.project_filter_search(CnFilter(cn)) if current is None: self.log.error("project_modify did not find project with cn = %s (dn = %s)" % (cn, dn)) raise NoSuchProjectError(cn) self.log.debug("project_modify current attribute values = %s - new attribute values = %s" % (current[0], attributes)) self.__modify(current[0], dn, attributes) def user_add(self, cn, attributes): """Add the values for the given attributes. @type cn: string representing the common name for the user. Together with the subtree, this forms the dn. @type attributes: dictionary with attributes for which a value should be added """ dn = "cn=%s,%s" % (cn, self.configuration.user_dn_base) self.ldap.add(dn, attributes.items()) def group_add(self, cn, attributes): """Add the values for the given attributes. @type cn: string representing the common name for the group. Together with the subtree, this forms the dn. @type attributes: dictionary with attributes for which a value should be added """ dn = "cn=%s,%s" % (cn, self.configuration.group_dn_base) self.ldap.add(dn, attributes.items()) def project_add(self, cn, attributes): """Add the values for the given attributes. @type cn: string representing the common name for the project. Together with the subtree, this forms the dn. @type attributes: dictionary with attributes for which a value should be added """ dn = "cn=%s,%s" % (cn, self.configuration.project_dn_base) self.ldap.add(dn, attributes.items()) def get_schema(self, ldap_obj_class_name_or_oid, reload=False): """Get attributes as provided by schema - we only do this once when it is requested - we cache the result as this should not change in a single run - unless the reload flag is set :-D @type ldap_obj_class_name_or_oid: LDAP class name or OID to get the schema from (passed to ldap.schema.subentry.SubSchema) @type reload: boolean indicating if the schema should be returned form the cache or reloaded from the LDAP server. @returns: dictionary containing details about the schema there is an entry for each attribute with the following keys - ['oid'] gives the corresponding oid for the attribute - ['single'] gives True for a single-value attribute, False otherwise @raise: LDAPError when the schema cannot be fetched for the given object class """ if self.ldap is None: self.bind() if not reload and ldap_obj_class_name_or_oid in self.schema: return self.schema[ldap_obj_class_name_or_oid] self.schema[ldap_obj_class_name_or_oid] = {} try: # returns ('cn=Subschema', <ldap.schema.subentry.SubSchema instance at 0x1986878>) schematype, schema = ldap.schema.subentry.urlfetch(self.ldap.ldap_url.unparse()) except ldap.LDAPError, _: self.log.raiseException("Failed to fetch schema from url %s" % (self.ldap.ldap_url)) attributes = {} if schematype == 'cn=Subschema': try: # this returns a list of dicts for x in schema.attribute_types([ldap_obj_class_name_or_oid]): attributes.update(x) except Exception, _: self.log.raiseException("Failed to retrieve attributes from schematype %s and ldap_obj_class_name_or_oid %s" % (schematype, ldap_obj_class_name_or_oid))
import sys, pprint, ldap, ldap.schema schema_attrs = ldap.schema.SCHEMA_ATTRS ldap.set_option(ldap.OPT_DEBUG_LEVEL, 0) ldap._trace_level = 0 subschemasubentry_dn, schema = ldap.schema.urlfetch(sys.argv[-1]) if subschemasubentry_dn is None: print "No sub schema sub entry found!" sys.exit(1) oc_list = ["person", "organizationalPerson", "inetOrgPerson"] struct_oc = schema.get_structural_oc(oc_list) # print str(schema.get_obj(ldap.schema.ObjectClass,struct_oc)) pprint.pprint(schema.get_applicable_aux_classes(struct_oc)) pprint.pprint(schema.attribute_types(oc_list))