Example #1
0
 def toTupleList(self):
     """
     Convert the attrs and values to a list of 2-tuples.  The first
     element of the tuple is the attribute name.  The second element
     is either a single value or a list of values.
     """
     # For python3, we have to make sure EVERYTHING is a byte string.
     # Else everything EXPLODES
     lt = None
     if MAJOR >= 3:
         # This converts the dict to a list of tuples,
         lt = []
         for k in self.data.keys():
             # l here is the
             vals = None
             if isinstance(self.data[k], list) or isinstance(
                     self.data[k], tuple):
                 vals = ensure_list_bytes(self.data[k])
             else:
                 vals = ensure_list_bytes([self.data[k]])
             lt.append((k, vals))
         # lt is now complete.
     else:
         lt = list(self.data.items())
     return lt
Example #2
0
    def apply_mods(self, mods):
        """Perform modification operation using several mods at once

        @param mods - list of tuples:  [(action, key, value),]
        @raise ValueError - if a provided mod op is invalid
        @raise LDAPError
        """
        mod_list = []
        for mod in mods:
            if len(mod) < 2:
                # Error
                raise ValueError('Not enough arguments in the mod op')
            elif len(mod) == 2:  # no action
                action = ldap.MOD_REPLACE
                key, value = mod
            elif len(mod) == 3:
                action, key, value = mod
                if action != ldap.MOD_REPLACE or \
                   action != ldap.MOD_ADD or \
                   action != ldap.MOD_DELETE:
                    raise ValueError('Invalid mod action(%s)' % str(action))
            else:
                # Error too many items
                raise ValueError('Too many arguments in the mod op')

            if isinstance(value, list):
                value = ensure_list_bytes(value)
            else:
                value = [ensure_bytes(value)]

            mod_list.append((action, key, value))
        return self._instance.modify_s(self._dn, mod_list)
    def set(self, key, value, action=ldap.MOD_REPLACE):
        """Perform a specified action on a key with value

        :param key: an attribute name
        :type key: str
        :param value: an attribute value
        :type value: str
        :param action: - ldap.MOD_REPLACE - by default
                        - ldap.MOD_ADD
                        - ldap.MOD_DELETE
        :type action: int

        :returns: result of modify_s operation
        :raises: ValueError - if instance is not online
        """

        if value is None or len(value) < 512:
            self._log.debug("%s set(%r, %r)" % (self._dn, key, value))
        else:
            self._log.debug("%s set(%r, value too large)" % (self._dn, key))
        if self._instance.state != DIRSRV_STATE_ONLINE:
            raise ValueError(
                "Invalid state. Cannot set properties on instance that is not ONLINE."
            )

        if isinstance(value, list):
            # value = map(lambda x: ensure_bytes(x), value)
            value = ensure_list_bytes(value)
        elif value is not None:
            value = [ensure_bytes(value)]

        return self._instance.modify_ext_s(self._dn, [(action, key, value)],
                                           serverctrls=self._server_controls,
                                           clientctrls=self._client_controls)
Example #4
0
    def _validate(self, rdn, properties, basedn):
        """
        Used to validate a create request.
        This way, it can be over-ridden without affecting
        the create types

        It also checks that all the values in _must_attribute exist
        in some form in the dictionary

        It has the useful trick of returning the dn, so subtypes
        can use extra properties to create the dn's here for this.
        """
        if properties is None:
            raise ldap.UNWILLING_TO_PERFORM('Invalid request to create. Properties cannot be None')
        if type(properties) != dict:
            raise ldap.UNWILLING_TO_PERFORM("properties must be a dictionary")

        # I think this needs to be made case insensitive
        # How will this work with the dictionary?
        for attr in self._must_attributes:
            if properties.get(attr, None) is None:
                raise ldap.UNWILLING_TO_PERFORM('Attribute %s must not be None' % attr)

        # Make sure the naming attribute is present
        if properties.get(self._rdn_attribute, None) is None and rdn is None:
            raise ldap.UNWILLING_TO_PERFORM('Attribute %s must not be None or rdn provided' % self._rdn_attribute)
        
        # This change here, means we can pre-load a full dn to _dn, or we can
        # accept based on the rdn
        tdn = self._dn

        if tdn is None:
            if basedn is None:
                raise ldap.UNWILLING_TO_PERFORM('Invalid request to create. basedn cannot be None')

            if properties.get(self._rdn_attribute, None) is not None:
                # Favour the value in the properties dictionary
                v = properties.get(self._rdn_attribute)
                if isinstance(v, list):
                    rdn = ensure_str(v[0])
                else:
                    rdn = ensure_str(v)

                tdn = '%s=%s,%s' % (self._rdn_attribute, rdn, basedn)

        # We may need to map over the data in the properties dict to satisfy python-ldap
        str_props = {}
        for k, v in properties.items():
            if isinstance(v, list):
                # str_props[k] = map(lambda v1: ensure_bytes(v1), v)
                str_props[k] = ensure_list_bytes(v)
            else:
                str_props[k] = ensure_bytes(v)
        #
        # Do we need to do extra dn validation here?
        return (tdn, str_props)
    def _create(self, rdn=None, properties=None, basedn=None, ensure=False):
        """Internal implementation of create. This is used by ensure
        and create, to prevent code duplication. You should *never* call
        this method directly.
        """
        assert (len(self._create_objectclasses) > 0)
        basedn = ensure_str(basedn)
        self._log.debug('Checking "%s" under %s : %s' %
                        (rdn, basedn, properties))
        # Add the objectClasses to the properties
        (dn, valid_props) = self._validate(rdn, properties, basedn)
        # Check if the entry exists or not? .add_s is going to error anyway ...
        self._log.debug('Validated dn %s : valid_props %s' % (dn, valid_props))

        exists = False

        try:
            self._instance.search_ext_s(dn,
                                        ldap.SCOPE_BASE,
                                        self._object_filter,
                                        attrsonly=1,
                                        serverctrls=self._server_controls,
                                        clientctrls=self._client_controls)
            exists = True
        except ldap.NO_SUCH_OBJECT:
            pass

        if exists and ensure:
            # update properties
            self._log.debug('Exists %s' % dn)
            self._dn = dn
            # Now use replace_many to setup our values
            mods = []
            for k, v in valid_props.items():
                mods.append((ldap.MOD_REPLACE, k, v))
            self._instance.modify_ext_s(self._dn,
                                        mods,
                                        serverctrls=self._server_controls,
                                        clientctrls=self._client_controls)
        elif exists and not ensure:
            # raise "already exists."
            raise ldap.ALREADY_EXISTS("Entry %s already exists" % dn)
        if not exists:
            self._log.debug('Creating %s' % dn)
            e = Entry(dn)
            e.update(
                {'objectclass': ensure_list_bytes(self._create_objectclasses)})
            e.update(valid_props)
            # We rely on exceptions here to indicate failure to the parent.
            self._log.debug('Creating entry %s : %s' % (dn, e))
            self._instance.add_ext_s(e,
                                     serverctrls=self._server_controls,
                                     clientctrls=self._client_controls)
            # If it worked, we need to fix our instance dn
            self._dn = dn
        return self
Example #6
0
    def get_attr_vals_bytes(self, key, use_json=False):
        """Get attribute values from the entry in bytes type

        :param key: An attribute name
        :type key: str
        :returns: A single bytes value
        :raises: ValueError - if instance is offline
        """

        return ensure_list_bytes(self.get_attr_vals(key))
Example #7
0
    def _create(self, rdn=None, properties=None, basedn=None, ensure=False):
        """Internal implementation of create. This is used by ensure
        and create, to prevent code duplication. You should *never* call
        this method directly.
        """
        assert(len(self._create_objectclasses) > 0)
        basedn = ensure_str(basedn)
        self._log.debug('Checking "%s" under %s : %s' % (rdn, basedn, display_log_data(properties)))
        # Add the objectClasses to the properties
        (dn, valid_props) = self._validate(rdn, properties, basedn)
        # Check if the entry exists or not? .add_s is going to error anyway ...
        self._log.debug('Validated dn {}'.format(dn))

        exists = False

        if ensure:
            # If we are running in stateful ensure mode, we need to check if the object exists, and
            # we can see the state that it is in.
            try:
                self._instance.search_ext_s(dn, ldap.SCOPE_BASE, self._object_filter, attrsonly=1, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
                exists = True
            except ldap.NO_SUCH_OBJECT:
                pass

        if exists and ensure:
            # update properties
            self._log.debug('Exists %s' % dn)
            self._dn = dn
            # Now use replace_many to setup our values
            mods = []
            for k, v in list(valid_props.items()):
                mods.append((ldap.MOD_REPLACE, k, v))
            self._instance.modify_ext_s(self._dn, mods, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
        elif not exists:
            # This case is reached in two cases. One is we are in ensure mode, and we KNOW the entry
            # doesn't exist.
            # The alternate, is that we are in a non-stateful create, so we "just create" and see
            # what happens. I believe the technical term is "yolo create".
            self._log.debug('Creating %s' % dn)
            e = Entry(dn)
            e.update({'objectclass': ensure_list_bytes(self._create_objectclasses)})
            e.update(valid_props)
            # We rely on exceptions here to indicate failure to the parent.
            self._instance.add_ext_s(e, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
            self._log.debug('Created entry %s : %s' % (dn, display_log_data(e.data)))
            # If it worked, we need to fix our instance dn for the object's self reference. Because
            # we may not have a self reference yet (just created), it may have changed (someone
            # set dn, but validate altered it).
            self._dn = dn
        else:
            # This case can't be reached now that we only check existance on ensure.
            # However, it's good to keep it for "complete" behaviour, exhausting all states.
            # We could highlight bugs ;)
            raise AssertionError("Impossible State Reached in _create")
        return self
Example #8
0
    def set(self, key, value, action=ldap.MOD_REPLACE):
        self._log.debug("%s set(%r, %r)" % (self._dn, key, value))
        if self._instance.state != DIRSRV_STATE_ONLINE:
            raise ValueError("Invalid state. Cannot set properties on instance that is not ONLINE.")

        if isinstance(value, list):
            # value = map(lambda x: ensure_bytes(x), value)
            value = ensure_list_bytes(value)
        else:
            value = [ensure_bytes(value)]

        if self._batch:
            pass
        else:
            return self._instance.modify_s(self._dn, [(action, key, value)])
Example #9
0
    def create(self, rdn=None, properties=None, basedn=None):
        assert(len(self._create_objectclasses) > 0)
        self._log.debug('Creating %s %s : %s' % (rdn, basedn, properties))
        # Add the objectClasses to the properties
        (dn, valid_props) = self._validate(rdn, properties, basedn)
        # Check if the entry exists or not? .add_s is going to error anyway ...
        self._log.debug('Validated %s : %s' % (dn, valid_props))

        e = Entry(dn)
        e.update({'objectclass': ensure_list_bytes(self._create_objectclasses)})
        e.update(valid_props)
        # We rely on exceptions here to indicate failure to the parent.
        self._log.debug('Creating entry %s : %s' % (dn, e))
        self._instance.add_s(e)
        # If it worked, we need to fix our instance dn
        self._dn = dn
        return self
Example #10
0
    def __repr__(self):
        """Convert the Entry to its LDIF representation"""
        sio = six.StringIO()
        """
        what's all this then?  the unparse method will currently only accept
        a list or a dict, not a class derived from them.  self.data is a
        cidict, so unparse barfs on it.  I've filed a bug against
        python-ldap, but in the meantime, we have to convert to a plain old
        dict for printing.
        I also don't want to see wrapping, so set the line width really high
        (1000)
        """
        newdata = {}
        for k, v in self.data.items():
            newdata[k] = ensure_list_bytes(v)

        ldif.LDIFWriter(
            sio, Entry.base64_attrs, 1000).unparse(self.dn, newdata)
        return sio.getvalue()
Example #11
0
    def replace_many(self, *args):
        """Replace many key, value pairs in a single operation.
        This is useful for configuration changes that require
        atomic operation, and ease of use.

        An example of usage is replace_many((key, value), (key, value))

        No wrapping list is needed for the arguments.

        :param *args: tuples of key,value to replace.
        :type *args: (str, str)
        """

        mods = []
        for arg in args:
            if isinstance(arg[1], list) or isinstance(arg[1], tuple):
                value = ensure_list_bytes(arg[1])
            else:
                value = [ensure_bytes(arg[1])]
            mods.append((ldap.MOD_REPLACE, ensure_str(arg[0]), value))
        return self._instance.modify_ext_s(self._dn, mods, serverctrls=self._server_controls,
                                           clientctrls=self._client_controls, escapehatch='i am sure')
Example #12
0
    def apply_mods(self, mods):
        """Perform modification operation using several mods at once

        :param mods: [(action, key, value),] or [(ldap.MOD_DELETE, key),]
        :type mods: list of tuples
        :raises: ValueError - if a provided mod op is invalid
        """

        mod_list = []
        for mod in mods:
            if len(mod) < 2:
                # Error
                raise ValueError('Not enough arguments in the mod op')
            elif len(mod) == 2:  # no action
                # This hack exists because the original lib389 Entry type
                # does odd things.
                action, key = mod
                if action != ldap.MOD_DELETE:
                    raise ValueError('Only MOD_DELETE takes two arguments %s' % mod)
                value = None
                # Just add the raw mod, because we don't have a value
                mod_list.append((action, key, value))
            elif len(mod) == 3:
                action, key, value = mod
                if action != ldap.MOD_REPLACE and \
                   action != ldap.MOD_ADD and \
                   action != ldap.MOD_DELETE:
                    raise ValueError('Invalid mod action(%s)' % str(action))
                if isinstance(value, list):
                    value = ensure_list_bytes(value)
                else:
                    value = [ensure_bytes(value)]
                mod_list.append((action, key, value))
            else:
                # Error too many items
                raise ValueError('Too many arguments in the mod op')
        return self._instance.modify_ext_s(self._dn, mod_list, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
    def apply_mods(self, mods):
        """Perform modification operation using several mods at once

        :param mods: [(action, key, value),]
        :type mods: list of tuples
        :raises: ValueError - if a provided mod op is invalid
        """

        mod_list = []
        for mod in mods:
            if len(mod) < 2:
                # Error
                raise ValueError('Not enough arguments in the mod op')
            elif len(mod) == 2:  # no action
                action = ldap.MOD_REPLACE
                key, value = mod
            elif len(mod) == 3:
                action, key, value = mod
                if action != ldap.MOD_REPLACE and \
                   action != ldap.MOD_ADD and \
                   action != ldap.MOD_DELETE:
                    raise ValueError('Invalid mod action(%s)' % str(action))
            else:
                # Error too many items
                raise ValueError('Too many arguments in the mod op')

            if isinstance(value, list):
                value = ensure_list_bytes(value)
            else:
                value = [ensure_bytes(value)]

            mod_list.append((action, key, value))
        return self._instance.modify_ext_s(self._dn,
                                           mod_list,
                                           serverctrls=self._server_controls,
                                           clientctrls=self._client_controls)
Example #14
0
    def _validate(self, rdn, properties, basedn):
        """Used to validate a create request.
        This way, it can be over-ridden without affecting
        the create types.

        It also checks that all the values in _must_attribute exist
        in some form in the dictionary.

        It has the useful trick of returning the dn, so subtypes
        can use extra properties to create the dn's here for this.
        """

        if properties is None:
            raise ldap.UNWILLING_TO_PERFORM('Invalid request to create. Properties cannot be None')
        if type(properties) != dict:
            raise ldap.UNWILLING_TO_PERFORM("properties must be a dictionary")

        # I think this needs to be made case insensitive
        # How will this work with the dictionary?
        if self._must_attributes is not None:
            for attr in self._must_attributes:
                if properties.get(attr, None) is None:
                    # Put RDN to properties
                    if attr == self._rdn_attribute and rdn is not None:
                        properties[self._rdn_attribute] = ldap.dn.str2dn(rdn)[0][0][1]
                    else:
                        raise ldap.UNWILLING_TO_PERFORM('Attribute %s must not be None' % attr)

        # Make sure the naming attribute is present
        if properties.get(self._rdn_attribute, None) is None and rdn is None:
            raise ldap.UNWILLING_TO_PERFORM('Attribute %s must not be None or rdn provided' % self._rdn_attribute)

        # Great! Now, lets fix up our types
        for k, v in properties.items():
            if isinstance(v, list):
                # Great!
                pass
            else:
                # Turn it into a list instead.
                properties[k] = [v, ]

        # If we were created with a dn= in the object init, we set tdn now, and skip
        # any possible dn derivation steps that follow.
        tdn = self._dn

        # However, if no DN was provided, we attempt to derive the DN from the relevant
        # properties of the object. The idea being that by defining only the attributes
        # of the object, we can just solve a possible rdn instead of asking for the same
        # data twice.
        if tdn is None:
            if basedn is None:
                raise ldap.UNWILLING_TO_PERFORM('Invalid request to create. basedn cannot be None')

            # Were we given a relative component? Yes? Go ahead!
            if rdn is not None:
                tdn = ensure_str('%s,%s' % (rdn, basedn))
            elif properties.get(self._rdn_attribute, None) is not None:
                # Favour the value in the properties dictionary
                v = properties.get(self._rdn_attribute)
                rdn = ensure_str(v[0])

                erdn = ensure_str(ldap.dn.escape_dn_chars(rdn))
                self._log.debug("Using first property %s: %s as rdn" % (self._rdn_attribute, erdn))
                # Now we compare. If we changed this value, we have to put it back to make the properties complete.
                if erdn != rdn:
                    properties[self._rdn_attribute].append(erdn)

                tdn = ensure_str('%s=%s,%s' % (self._rdn_attribute, erdn, basedn))

        # We may need to map over the data in the properties dict to satisfy python-ldap
        str_props = {}
        for k, v in properties.items():
            str_props[k] = ensure_list_bytes(v)
        #
        # Do we need to do extra dn validation here?
        return (tdn, str_props)
Example #15
0
def test_memberof_with_changelog_reset(topo_m2):
    """Test that replication does not break, after DS stop-start, due to changelog reset

    :id: 60c11636-55a1-4704-9e09-2c6bcc828de4
    :setup: 2 Masters
    :steps:
        1. On M1 and M2, Enable memberof
        2. On M1, add 999 entries allowing memberof
        3. On M1, add a group with these 999 entries as members
        4. Stop M1 in between,
           when add the group memerof is called and before it is finished the
           add, so step 4 should be executed after memberof has started and
           before the add has finished
        5. Check that replication is working fine
    :expectedresults:
        1. memberof should be enabled
        2. Entries should be added
        3. Add operation should start
        4. M1 should be stopped
        5. Replication should be working fine
    """
    m1 = topo_m2.ms["master1"]
    m2 = topo_m2.ms["master2"]

    log.info("Configure memberof on M1 and M2")
    memberof = MemberOfPlugin(m1)
    memberof.enable()
    memberof.set_autoaddoc('nsMemberOf')
    m1.restart()

    memberof = MemberOfPlugin(m2)
    memberof.enable()
    memberof.set_autoaddoc('nsMemberOf')
    m2.restart()

    log.info("On M1, add 999 test entries allowing memberof")
    users_list = add_users(topo_m2, 999, DEFAULT_SUFFIX)

    log.info("On M1, add a group with these 999 entries as members")
    dic_of_attributes = {
        'cn': ensure_bytes('testgroup'),
        'objectclass': ensure_list_bytes(['top', 'groupOfNames'])
    }

    for user in users_list:
        dic_of_attributes.setdefault('member', [])
        dic_of_attributes['member'].append(user.dn)

    log.info('Adding the test group using async function')
    groupdn = 'cn=testgroup,%s' % DEFAULT_SUFFIX
    m1.add(Entry((groupdn, dic_of_attributes)))

    #shutdown the server in-between adding the group
    m1.stop()

    #start the server
    m1.start()

    log.info("Check the log messages for error")
    error_msg = "ERR - NSMMReplicationPlugin - ruv_compare_ruv"
    assert not m1.ds_error_log.match(error_msg)

    log.info("Check that the replication is working fine both ways, M1 <-> M2")
    repl = ReplicationManager(DEFAULT_SUFFIX)
    repl.test_replication_topology(topo_m2)
    def _validate(self, rdn, properties, basedn):
        """Used to validate a create request.
        This way, it can be over-ridden without affecting
        the create types.

        It also checks that all the values in _must_attribute exist
        in some form in the dictionary.

        It has the useful trick of returning the dn, so subtypes
        can use extra properties to create the dn's here for this.
        """

        if properties is None:
            raise ldap.UNWILLING_TO_PERFORM(
                'Invalid request to create. Properties cannot be None')
        if type(properties) != dict:
            raise ldap.UNWILLING_TO_PERFORM("properties must be a dictionary")

        # I think this needs to be made case insensitive
        # How will this work with the dictionary?
        for attr in self._must_attributes:
            if properties.get(attr, None) is None:
                raise ldap.UNWILLING_TO_PERFORM(
                    'Attribute %s must not be None' % attr)

        # Make sure the naming attribute is present
        if properties.get(self._rdn_attribute, None) is None and rdn is None:
            raise ldap.UNWILLING_TO_PERFORM(
                'Attribute %s must not be None or rdn provided' %
                self._rdn_attribute)

        # Great! Now, lets fix up our types
        for k, v in properties.items():
            if isinstance(v, list):
                # Great!
                pass
            else:
                # Turn it into a list instead.
                properties[k] = [
                    v,
                ]

        # This change here, means we can pre-load a full dn to _dn, or we can
        # accept based on the rdn
        tdn = self._dn

        if tdn is None:
            if basedn is None:
                raise ldap.UNWILLING_TO_PERFORM(
                    'Invalid request to create. basedn cannot be None')

            if rdn is not None:
                tdn = ensure_str('%s,%s' % (rdn, basedn))
            elif properties.get(self._rdn_attribute, None) is not None:
                # Favour the value in the properties dictionary
                v = properties.get(self._rdn_attribute)
                rdn = ensure_str(v[0])

                erdn = ensure_str(ldap.dn.escape_dn_chars(rdn))
                self._log.debug("Using first property %s: %s as rdn" %
                                (self._rdn_attribute, erdn))
                # Now we compare. If we changed this value, we have to put it back to make the properties complete.
                if erdn != rdn:
                    properties[self._rdn_attribute].append(erdn)

                tdn = ensure_str('%s=%s,%s' %
                                 (self._rdn_attribute, erdn, basedn))

        # We may need to map over the data in the properties dict to satisfy python-ldap
        str_props = {}
        for k, v in properties.items():
            str_props[k] = ensure_list_bytes(v)
        #
        # Do we need to do extra dn validation here?
        return (tdn, str_props)