Ejemplo n.º 1
0
        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
Ejemplo n.º 2
0
    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
        }
Ejemplo n.º 3
0
    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'))
            )
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
 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
Ejemplo n.º 6
0
 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)
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
 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
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
    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'))
            )
Ejemplo n.º 12
0
def dn_from_username(username, base):
    return safe_dn([f"uid={username}", base])
Ejemplo n.º 13
0
def dn_from_cn(name, base):
    return safe_dn([f"cn={name}", base])
Ejemplo n.º 14
0
def username_to_dn(username):
    return safe_dn(["cn=%s" % username, app.config["LDAP_BASE_DN"]])
Ejemplo n.º 15
0
def dn_from_cn(name, base):
    return safe_dn(["cn={}".format(name), base])
Ejemplo n.º 16
0
def dn_from_username(username, base):
    return safe_dn(["uid={}".format(username), base])
Ejemplo n.º 17
0
    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'))
            )