123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- """
- """
-
- # Created on 2014.02.02
- #
- # Author: Giovanni Cannata
- #
- # Copyright 2014 - 2018 Giovanni Cannata
- #
- # This file is part of ldap3.
- #
- # ldap3 is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # ldap3 is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public License
- # along with ldap3 in the COPYING and COPYING.LESSER files.
- # If not, see <http://www.gnu.org/licenses/>.
-
- from os import linesep
-
- from .attrDef import AttrDef
- from ..core.exceptions import LDAPKeyError, LDAPObjectError, LDAPAttributeError, LDAPSchemaError
- from .. import STRING_TYPES, SEQUENCE_TYPES, Server, Connection
- from ..protocol.rfc4512 import SchemaInfo, constant_to_class_kind
- from ..protocol.formatters.standard import find_attribute_validator
- from ..utils.ciDict import CaseInsensitiveWithAliasDict
- from ..utils.config import get_config_parameter
- from ..utils.log import log, log_enabled, ERROR, BASIC, PROTOCOL, EXTENDED
-
-
- class ObjectDef(object):
- """Represent an object in the LDAP server. AttrDefs are stored in a dictionary; the key is the friendly name defined in AttrDef.
-
- AttrDefs can be added and removed using the += ad -= operators
-
- ObjectDef can be accessed either as a sequence and a dictionary. When accessed the whole AttrDef instance is returned
-
- """
- def __init__(self, object_class=None, schema=None, custom_validator=None, auxiliary_class=None):
- if object_class is None:
- object_class = []
-
- if not isinstance(object_class, SEQUENCE_TYPES):
- object_class = [object_class]
-
- if auxiliary_class is None:
- auxiliary_class = []
-
- if not isinstance(auxiliary_class, SEQUENCE_TYPES):
- auxiliary_class = [auxiliary_class]
-
- self.__dict__['_attributes'] = CaseInsensitiveWithAliasDict()
- self.__dict__['_custom_validator'] = custom_validator
- self.__dict__['_oid_info'] = []
-
- if isinstance(schema, Connection) and (schema._deferred_bind or schema._deferred_open): # probably a lazy connection, tries to bind
- schema._fire_deferred()
-
- if schema is not None:
- if isinstance(schema, Server):
- schema = schema.schema
- elif isinstance(schema, Connection):
- schema = schema.server.schema
- elif isinstance(schema, SchemaInfo):
- pass
- elif schema:
- error_message = 'unable to read schema'
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPSchemaError(error_message)
- if schema is None:
- error_message = 'schema not present'
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPSchemaError(error_message)
- self.__dict__['_schema'] = schema
-
- if self._schema:
- object_class = [schema.object_classes[name].name[0] for name in object_class] # uses object class names capitalized as in schema
- auxiliary_class = [schema.object_classes[name].name[0] for name in auxiliary_class]
- for object_name in object_class:
- if object_name:
- self._populate_attr_defs(object_name)
-
- for object_name in auxiliary_class:
- if object_name:
- self._populate_attr_defs(object_name)
-
- self.__dict__['_object_class'] = object_class
- self.__dict__['_auxiliary_class'] = auxiliary_class
-
- if log_enabled(BASIC):
- log(BASIC, 'instantiated ObjectDef: <%r>', self)
-
- def _populate_attr_defs(self, object_name):
- if object_name in self._schema.object_classes:
- object_schema = self._schema.object_classes[object_name]
- self.__dict__['_oid_info'].append(object_name + " (" + constant_to_class_kind(object_schema.kind) + ") " + str(object_schema.oid))
-
- if object_schema.superior:
- for sup in object_schema.superior:
- self._populate_attr_defs(sup)
- for attribute_name in object_schema.must_contain:
- self.add_from_schema(attribute_name, True)
- for attribute_name in object_schema.may_contain:
- if attribute_name not in self._attributes: # the attribute could already be defined as "mandatory" in a superclass
- self.add_from_schema(attribute_name, False)
- else:
- error_message = 'object class \'%s\' not defined in schema' % object_name
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPObjectError(error_message)
-
- def __repr__(self):
- if self._object_class:
- r = 'OBJ : ' + ', '.join(self._object_class) + linesep
- else:
- r = 'OBJ : <None>' + linesep
- if self._auxiliary_class:
- r += 'AUX : ' + ', '.join(self._auxiliary_class) + linesep
- else:
- r += 'AUX : <None>' + linesep
- r += 'OID: ' + ', '.join([oid for oid in self._oid_info]) + linesep
- r += 'MUST: ' + ', '.join(sorted([attr for attr in self._attributes if self._attributes[attr].mandatory])) + linesep
- r += 'MAY : ' + ', '.join(sorted([attr for attr in self._attributes if not self._attributes[attr].mandatory])) + linesep
-
- return r
-
- def __str__(self):
- return self.__repr__()
-
- def __getitem__(self, item):
- return self.__getattr__(item)
-
- def __getattr__(self, item):
- item = ''.join(item.split()).lower()
- if '_attributes' in self.__dict__:
- try:
- return self._attributes[item]
- except KeyError:
- error_message = 'key \'%s\' not present' % item
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPKeyError(error_message)
- else:
- error_message = 'internal _attributes property not defined'
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPKeyError(error_message)
-
- def __setattr__(self, key, value):
- error_message = 'object \'%s\' is read only' % key
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPObjectError(error_message)
-
- def __iadd__(self, other):
- self.add_attribute(other)
- return self
-
- def __isub__(self, other):
- if isinstance(other, AttrDef):
- self.remove_attribute(other.key)
- elif isinstance(other, STRING_TYPES):
- self.remove_attribute(other)
-
- return self
-
- def __iter__(self):
- for attribute in self._attributes:
- yield self._attributes[attribute]
-
- def __len__(self):
- return len(self._attributes)
-
- if str is not bytes: # Python 3
- def __bool__(self): # needed to make the objectDef appears as existing in "if cursor:" even if there are no entries
- return True
- else: # Python 2
- def __nonzero__(self):
- return True
-
- def __contains__(self, item):
- try:
- self.__getitem__(item)
- except KeyError:
- return False
-
- return True
-
- def add_from_schema(self, attribute_name, mandatory=False):
- attr_def = AttrDef(attribute_name)
- attr_def.validate = find_attribute_validator(self._schema, attribute_name, self._custom_validator)
- attr_def.mandatory = mandatory # in schema mandatory is specified in the object class, not in the attribute class
- if self._schema and self._schema.attribute_types and attribute_name in self._schema.attribute_types:
- attr_def.single_value = self._schema.attribute_types[attribute_name].single_value
- attr_def.oid_info = self._schema.attribute_types[attribute_name]
- self.add_attribute(attr_def)
-
- def add_attribute(self, definition=None):
- """Add an AttrDef to the ObjectDef. Can be called with the += operator.
- :param definition: the AttrDef object to add, can also be a string containing the name of attribute to add. Can be a list of both
-
- """
- conf_attributes_excluded_from_object_def = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_OBJECT_DEF')]
- if isinstance(definition, STRING_TYPES):
- self.add_from_schema(definition)
- elif isinstance(definition, AttrDef):
- if definition.key.lower() not in conf_attributes_excluded_from_object_def:
- if definition.key not in self._attributes:
- self._attributes[definition.key] = definition
- if definition.name and definition.name != definition.key:
- self._attributes.set_alias(definition.key, definition.name)
- other_names = [name for name in definition.oid_info.name if definition.key.lower() != name.lower()] if definition.oid_info else None
- if other_names:
- self._attributes.set_alias(definition.key, other_names)
-
- if not definition.validate:
- validator = find_attribute_validator(self._schema, definition.key, self._custom_validator)
- self._attributes[definition.key].validate = validator
- elif isinstance(definition, SEQUENCE_TYPES):
- for element in definition:
- self.add_attribute(element)
- else:
- error_message = 'unable to add element to object definition'
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPObjectError(error_message)
-
- def remove_attribute(self, item):
- """Remove an AttrDef from the ObjectDef. Can be called with the -= operator.
- :param item: the AttrDef to remove, can also be a string containing the name of attribute to remove
-
- """
- key = None
- if isinstance(item, STRING_TYPES):
- key = ''.join(item.split()).lower()
- elif isinstance(item, AttrDef):
- key = item.key.lower()
-
- if key:
- for attr in self._attributes:
- if key == attr.lower():
- del self._attributes[attr]
- break
- else:
- error_message = 'key \'%s\' not present' % key
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPKeyError(error_message)
- else:
- error_message = 'key type must be str or AttrDef not ' + str(type(item))
- if log_enabled(ERROR):
- log(ERROR, '%s for <%s>', error_message, self)
- raise LDAPAttributeError(error_message)
-
- def clear_attributes(self):
- """Empty the ObjectDef attribute list
-
- """
- self.__dict__['object_class'] = None
- self.__dict__['auxiliary_class'] = None
- self.__dict__['_attributes'] = dict()
|