def connect(username, password): server = Server('mock_server', get_info=OFFLINE_AD_2012_R2) connection = Connection(server, user=username, password=password, authentication=SIMPLE, read_only=True, client_strategy=MOCK_SYNC) for dn, attrs in self._entries.items(): dn = safe_dn(dn).lower() entry_added = connection.strategy.add_entry(dn, attrs) if not entry_added: raise Exception('Failed to add entry ' + dn) lower_keys = {key.lower(): key for key in attrs.keys()} if 'samaccountname' in lower_keys: account_name = attrs[lower_keys['samaccountname']][0] domain_start = dn.find('dc=') + 3 domain_end = dn.find(',', domain_start) domain = dn[domain_start:domain_end] connection.server.dit[ domain + '\\' + account_name] = connection.server.dit[dn] if 'userprincipalname' in lower_keys: principal_name = attrs[lower_keys['userprincipalname']][0] connection.server.dit[ principal_name] = connection.server.dit[dn] connection.bind() return connection
async def search(self, search_base: str, search_filter: str, search_scope: str='SUBTREE', dereference_aliases: str='ALWAYS', attributes: Optional[Union[str, List[str]]]=None, size_limit: int=0, time_limit: int=0, types_only: bool=False, auto_escape: bool=True, auto_encode: bool=True, schema=None, validator=None, check_names: bool=False, cookie=None, timeout: Optional[int]=None, get_operational_attributes: bool=False, page_size=0) -> Dict[str, Any]: if not self.is_bound: raise LDAPBindException('Must be bound') search_base = safe_dn(search_base) if not attributes: attributes = ['1.1'] elif attributes == '*': attributes = ['*'] if isinstance(attributes, str): attributes = [attributes] if get_operational_attributes and isinstance(attributes, list): attributes.append('+') elif get_operational_attributes and isinstance(attributes, tuple): attributes += ('+',) controls = [] if page_size: controls.append(paged_search_control(False, page_size, cookie)) if not controls: controls = None search_req = search_operation( search_base, search_filter, search_scope, dereference_aliases, attributes, size_limit, time_limit, types_only, auto_escape=auto_escape, auto_encode=auto_encode, schema=schema, validator=validator, check_names=check_names ) msg_id = self._next_msg_id ldap_msg = LDAPClientProtocol.encapsulate_ldap_message(msg_id, 'searchRequest', search_req, controls=controls) resp = self._proto.send(ldap_msg) if timeout: with async_timeout(timeout): await resp.wait() else: await resp.wait() try: cookie = resp.additional['controls']['1.2.840.113556.1.4.319']['value']['cookie'] except KeyError: cookie = None if not isinstance(resp.data, list): data = [] else: data = resp.data return { 'entries': data, 'cookie': cookie }
async def modify(self, dn: str, changes: Dict[str, List[tuple]], controls=None, auto_encode: bool=True): """ Modify attributes of entry - changes is a dictionary in the form {'attribute1': change), 'attribute2': [change, change, ...], ...} - change is (operation, [value1, value2, ...]) - operation is 0 (MODIFY_ADD), 1 (MODIFY_DELETE), 2 (MODIFY_REPLACE), 3 (MODIFY_INCREMENT) """ dn = safe_dn(dn) if not isinstance(changes, dict): raise LDAPChangeException('Changes is not a dict') if not changes: raise LDAPChangeException('Changes dict cannot be empty') modify_req = modify_operation(dn, changes, auto_encode, None, validator=None, check_names=False) msg_id = self._next_msg_id # Generate final LDAP ASN message ldap_msg = LDAPClientProtocol.encapsulate_ldap_message(msg_id, 'modifyRequest', modify_req, controls=controls) resp = self._proto.send(ldap_msg) await resp.wait() if resp.data['result'] != 0: raise LDAPModifyException( 'Failed to modify dn {0}. Msg {1} {2} {3}'.format(dn, resp.data['result'], resp.data.get('message'), resp.data.get('description')) )
def put(self, dn, entry): status = None self.conn.search(search_base=dn, search_filter="(objectClass=*)", search_scope=BASE, attributes="*") if len(self.conn.response) == 1: stored_object = self.conn.response[0] mods = modlist(stored_object["attributes"], entry, self.ignore_attr_types) if mods: if self.debug: logging.info("update: %s" % (dn)) logging.info(mods) if not self.dryrun: self.conn.modify(dn, mods) pass status = 'update' else: if self.debug: logging.info("no-change: %s" % (dn)) status = 'nochange' else: if self.debug: logging.info("add: %s" % (dn)) entry2 = {} for k, v in entry.items(): if k not in self.ignore_attr_types: entry2[k] = v if not self.dryrun: self.conn.add(dn, attributes=entry2) status = 'add' self.seen[safe_dn(dn)] = True return status
def __init__(self, conn, base, scope=LEVEL, dryrun=False, debug=False, ignore_attr_types=[]): self.conn = conn self.base = base self.scope = scope self.dryrun = dryrun self.debug = debug self.ignore_attr_types = ignore_attr_types self.seen = {} self.seen[safe_dn(base)] = True
def generate_dn_from_entry(self): rdn_list = list() for key, attr in self._attributes.items(): if attr.name in self.entry_rdn: if len(self._attributes[key]) == 1: rdn = '{attr}={value}'.format( attr=attr.name, value=self._attributes[key].value) rdn_list.append(rdn) dn = '{rdn},{base_dn}'.format(rdn='+'.join(rdn_list), base_dn=self.base_dn) self._dn = safe_dn(dn)
def __init__(self, **kwargs): class _DummyCursor(object): # needed for _EntryState def __init__(self, object_def): self.definition = object_def if self.dn is None: raise NotImplementedError("%s must set the 'dn' attribute" % self.__class__) cursor = _DummyCursor(ObjectDef(self.object_classes)) self.__dict__["_state"] = EntryState(None, cursor) # TODO: remove the following line once ldap3 pull request #718 is merged if not hasattr(self._state, "entry_raw_attributes"): self._state.entry_raw_attributes = CaseInsensitiveWithAliasDict() # initialize attributes from kwargs attrdefs = dict(self._attrdefs) for k, v in iteritems(kwargs): if k in self._attrdefs: attrdef = attrdefs.pop(k) self._create_attribute_or_parameter(attrdef, v) else: raise TypeError("__init__() got an unexpected keyword argument" " '%s'" % k) # check remaining attributes for key in list(attrdefs): if attrdefs[key].default != NotImplemented: attrdef = attrdefs.pop(key) self._create_attribute_or_parameter(attrdef, attrdef.default) elif not attrdefs[key].mandatory: # delete non mandatory attrdef and add to definition self._state.definition += attrdefs.pop(key) # all remaining attributes are mandatory, do not provide a reasonable # default value (NotImplemented) and should have been set earlier if attrdefs: s = " '" if len(attrdefs) == 1 else "s '" raise TypeError("__init__() missing the following keyword " "argument" + s + ", ".join(attrdefs.keys()) + "'") # self._state will be overwritten by _Entry.__init__ # thus store a copy self._state state = self._state fmtdict = dict( (k, getattr(self.__class__, k)) for k in dir(self.__class__)) fmtdict.update(self._state.attributes) fmtdict.update(self._state.parameters) safedn = safe_dn(self.dn.format(**fmtdict)) _Entry.__init__(self, safedn, cursor) state.dn = safedn state.set_status(_STATUS_WRITEABLE) # restore self._state self.__dict__["_state"] = state
def generate_dn_from_entry(self): rdn_list = list() for attr in self._object_def: if attr.name in self.entry_rdn: if len(self._attributes[attr.key]) == 1: rdn = '{attr}={value}'.format( attr=attr.name, value=self._attributes[attr.key].value) rdn_list.append(rdn) dn = '{rdn},{base_dn}'.format(rdn='+'.join(rdn_list), base_dn=self.base_dn) self.__dict__['_dn'] = safe_dn(dn)
def purge(self): delete = [] results = self.conn.extend.standard.paged_search(self.base, "(objectClass=*)", self.scope, attributes=[]) for entry in results: dn = entry["dn"] if safe_dn(dn) not in self.seen: delete.append(dn) delete.sort(key=rev_dn, reverse=True) for dn in delete: if self.debug: logging.info("delete: %s" % (dn)) if not self.dryrun: self.conn.delete(dn) return delete
def generate_dn_from_entry(self): rdn_list = list() for attr in self._object_def: if attr.name in self.entry_rdn: if len(self._attributes[attr.key]) == 1: rdn = '{attr}={value}'.format( attr=attr.name, value=self._attributes[attr.key].value ) rdn_list.append(rdn) dn = '{rdn},{base_dn}'.format(rdn='+'.join(rdn_list), base_dn=self.base_dn) self.__dict__['_dn'] = safe_dn(dn)
async def delete(self, dn: str, controls=None, ignore_no_exist=False): """ Delete the entry identified by the DN from the DIB. """ dn = safe_dn(dn) del_req = delete_operation(dn) msg_id = self._next_msg_id # Generate final LDAP ASN message ldap_msg = LDAPClientProtocol.encapsulate_ldap_message(msg_id, 'delRequest', del_req, controls=controls) resp = self._proto.send(ldap_msg) await resp.wait() if resp.data['result'] != 0 and not (ignore_no_exist and resp.data['result'] == 32): raise LDAPDeleteException( 'Failed to modify dn {0}. Msg {1} {2} {3}'.format(dn, resp.data['result'], resp.data.get('message'), resp.data.get('description')) )
def dn_from_username(username, base): return safe_dn([f"uid={username}", base])
def dn_from_cn(name, base): return safe_dn([f"cn={name}", base])
def username_to_dn(username): return safe_dn(["cn=%s" % username, app.config["LDAP_BASE_DN"]])
def dn_from_cn(name, base): return safe_dn(["cn={}".format(name), base])
def dn_from_username(username, base): return safe_dn(["uid={}".format(username), base])
async def add(self, dn: str, object_class: Optional[Union[List[str], str]]=None, attributes: Dict[str, Union[List[str], str]]=None, controls=None, auto_encode: bool=True, timeout: Optional[int]=None): """ Add dn to the DIT, object_class is None, a class name or a list of class names. Attributes is a dictionary in the form 'attr': 'val' or 'attr': ['val1', 'val2', ...] for multivalued attributes """ _attributes = deepcopy(attributes) # dict could change when adding objectClass values dn = safe_dn(dn) attr_object_class = [] if object_class is not None: if isinstance(object_class, str): attr_object_class.append(object_class) else: attr_object_class.extend(object_class) # Look through attributes to see if object classes are specified there object_class_attr_name = '' if _attributes: for attr in _attributes: if attr.lower() == 'objectclass': object_class_attr_name = attr obj_class_val = _attributes[object_class_attr_name] if isinstance(obj_class_val, str): attr_object_class.append(obj_class_val) else: attr_object_class.extend(obj_class_val) break else: _attributes = {} if not object_class_attr_name: object_class_attr_name = 'objectClass' # So now we have attr_object_class, which contains any passed in object classes and any we've found in attributes. # Converts objectclass to unicode in case of bytes value, also removes dupes attr_object_class = list(set([to_unicode(object_class) for object_class in attr_object_class])) _attributes[object_class_attr_name] = attr_object_class add_request = add_operation(dn, _attributes, auto_encode, None, validator=None, check_names=False) msg_id = self._next_msg_id # Generate final LDAP ASN message ldap_msg = LDAPClientProtocol.encapsulate_ldap_message(msg_id, 'addRequest', add_request, controls=controls) resp = self._proto.send(ldap_msg) if timeout: with async_timeout(timeout): await resp.wait() else: await resp.wait() if resp.data['result'] != 0: raise LDAPAddException( 'Failed to modify dn {0}. Msg {1} {2} {3}'.format(dn, resp.data['result'], resp.data.get('message'), resp.data.get('description')) )