コード例 #1
0
def _dummy_modify(connect_spec, dn, directives):
    assert dn in db
    e = db[dn]
    for op, attr, vals in directives:
        if op == 'add':
            assert len(vals)
            existing_vals = e.setdefault(attr, OrderedSet())
            for val in vals:
                assert val not in existing_vals
                existing_vals.add(val)
        elif op == 'delete':
            assert attr in e
            existing_vals = e[attr]
            assert len(existing_vals)
            if not len(vals):
                del e[attr]
                continue
            for val in vals:
                assert val in existing_vals
                existing_vals.remove(val)
            if not len(existing_vals):
                del e[attr]
        elif op == 'replace':
            e.pop(attr, None)
            e[attr] = OrderedSet(vals)
        else:
            raise ValueError()
    return True
コード例 #2
0
 def dummy_modify(self, connect_spec, dn, directives):
     assert dn in self.db
     e = self.db[dn]
     for op, attr, vals in directives:
         if op == "add":
             assert vals
             existing_vals = e.setdefault(attr, OrderedSet())
             for val in vals:
                 assert val not in existing_vals
                 existing_vals.add(val)
         elif op == "delete":
             assert attr in e
             existing_vals = e[attr]
             assert existing_vals
             if not vals:
                 del e[attr]
                 continue
             for val in vals:
                 assert val in existing_vals
                 existing_vals.remove(val)
             if not existing_vals:
                 del e[attr]
         elif op == "replace":
             e.pop(attr, None)
             e[attr] = OrderedSet(vals)
         else:
             raise ValueError()
     return True
コード例 #3
0
ファイル: ldap.py プロジェクト: zhengyu1992/salt
def _toset(thing):
    '''helper to convert various things to a set

    This enables flexibility in what users provide as the list of LDAP
    entry attribute values.  Note that the LDAP spec prohibits
    duplicate values in an attribute.

    RFC 2251 states that:
    "The order of attribute values within the vals set is undefined and
     implementation-dependent, and MUST NOT be relied upon."
    However, OpenLDAP have an X-ORDERED that is used in the config schema.
    Using sets would mean we can't pass ordered values and therefore can't
    manage parts of the OpenLDAP configuration, hence the use of OrderedSet.

    Sets are also good for automatically removing duplicates.

    None becomes an empty set.  Iterables except for strings have
    their elements added to a new set.  Non-None scalars (strings,
    numbers, non-iterable objects, etc.) are added as the only member
    of a new set.

    '''
    if thing is None:
        return OrderedSet()
    if isinstance(thing, six.string_types):
        return OrderedSet((thing, ))
    # convert numbers to strings so that equality checks work
    # (LDAP stores numbers as strings)
    try:
        return OrderedSet((str(x) for x in thing))
    except TypeError:
        return OrderedSet((str(thing), ))
コード例 #4
0
def _dummy_add(connect_spec, dn, attributes):
    assert dn not in db
    assert len(attributes)
    db[dn] = {}
    for attr, vals in six.iteritems(attributes):
        assert len(vals)
        db[dn][attr] = OrderedSet(vals)
    return True
コード例 #5
0
def _complex_db():
    return {
        'dnfoo': {
            'attrfoo1': OrderedSet((
                'valfoo1.1',
                'valfoo1.2',
            )),
            'attrfoo2': OrderedSet(('valfoo2.1', )),
        },
        'dnbar': {
            'attrbar1': OrderedSet((
                'valbar1.1',
                'valbar1.2',
            )),
            'attrbar2': OrderedSet(('valbar2.1', )),
        },
    }
コード例 #6
0
def _dummy_add(connect_spec, dn, attributes):
    assert dn not in db
    assert attributes
    db[dn] = {}
    for attr, vals in attributes.items():
        assert vals
        db[dn][attr] = OrderedSet(vals)
    return True
コード例 #7
0
ファイル: test_ldap.py プロジェクト: fake-name/salt
def _complex_db():
    return {
        "dnfoo": {
            "attrfoo1": OrderedSet((
                "valfoo1.1",
                "valfoo1.2",
            )),
            "attrfoo2": OrderedSet(("valfoo2.1", )),
        },
        "dnbar": {
            "attrbar1": OrderedSet((
                "valbar1.1",
                "valbar1.2",
            )),
            "attrbar2": OrderedSet(("valbar2.1", )),
        },
    }
コード例 #8
0
def no_change_complex_db(db):
    db.db = {
        "dnfoo": {
            "attrfoo1": OrderedSet((
                b"valfoo1.1",
                b"valfoo1.2",
            )),
            "attrfoo2": OrderedSet((b"valfoo2.1", )),
        },
        "dnbar": {
            "attrbar1": OrderedSet((
                b"valbar1.1",
                b"valbar1.2",
            )),
            "attrbar2": OrderedSet((b"valbar2.1", )),
        },
    }
    return db
コード例 #9
0
def _dummy_change(connect_spec, dn, before, after):
    assert before != after
    assert len(before)
    assert len(after)
    assert dn in db
    e = db[dn]
    assert e == before
    all_attrs = OrderedSet()
    all_attrs.update(before)
    all_attrs.update(after)
    directives = []
    for attr in all_attrs:
        if attr not in before:
            assert attr in after
            assert len(after[attr])
            directives.append(('add', attr, after[attr]))
        elif attr not in after:
            assert attr in before
            assert len(before[attr])
            directives.append(('delete', attr, ()))
        else:
            assert len(before[attr])
            assert len(after[attr])
            to_del = before[attr] - after[attr]
            if len(to_del):
                directives.append(('delete', attr, to_del))
            to_add = after[attr] - before[attr]
            if len(to_add):
                directives.append(('add', attr, to_add))
    return _dummy_modify(connect_spec, dn, directives)
コード例 #10
0
 def dummy_change(self, connect_spec, dn, before, after):
     assert before != after
     assert before
     assert after
     assert dn in self.db
     e = self.db[dn]
     assert e == before
     all_attrs = OrderedSet()
     all_attrs.update(before)
     all_attrs.update(after)
     directives = []
     for attr in all_attrs:
         if attr not in before:
             assert attr in after
             assert after[attr]
             directives.append(("add", attr, after[attr]))
         elif attr not in after:
             assert attr in before
             assert before[attr]
             directives.append(("delete", attr, ()))
         else:
             assert before[attr]
             assert after[attr]
             to_del = before[attr] - after[attr]
             if to_del:
                 directives.append(("delete", attr, to_del))
             to_add = after[attr] - before[attr]
             if to_add:
                 directives.append(("add", attr, to_add))
     return self.dummy_modify(connect_spec, dn, directives)
コード例 #11
0
ファイル: ldap.py プロジェクト: mamh-mixed/saltstack-salt
def _update_entry(entry, status, directives):
    """Update an entry's attributes using the provided directives

    :param entry:
        A dict mapping each attribute name to a set of its values
    :param status:
        A dict holding cross-invocation status (whether delete_others
        is True or not, and the set of mentioned attributes)
    :param directives:
        A dict mapping directive types to directive-specific state
    """
    for directive, state in directives.items():
        if directive == "delete_others":
            status["delete_others"] = state
            continue
        for attr, vals in state.items():
            status["mentioned_attributes"].add(attr)
            vals = _toset(vals)
            if directive == "default":
                if vals and (attr not in entry or not entry[attr]):
                    entry[attr] = vals
            elif directive == "add":
                vals.update(entry.get(attr, OrderedSet()))
                if vals:
                    entry[attr] = vals
            elif directive == "delete":
                existing_vals = entry.pop(attr, OrderedSet())
                if vals:
                    existing_vals -= vals
                    if existing_vals:
                        entry[attr] = existing_vals
            elif directive == "replace":
                entry.pop(attr, None)
                if vals:
                    entry[attr] = vals
            else:
                raise ValueError("unknown directive: " + directive)
コード例 #12
0
ファイル: ldap.py プロジェクト: zhengyu1992/salt
def _update_entry(entry, status, directives):
    '''Update an entry's attributes using the provided directives

    :param entry:
        A dict mapping each attribute name to a set of its values
    :param status:
        A dict holding cross-invocation status (whether delete_others
        is True or not, and the set of mentioned attributes)
    :param directives:
        A dict mapping directive types to directive-specific state
    '''
    for directive, state in six.iteritems(directives):
        if directive == 'delete_others':
            status['delete_others'] = state
            continue
        for attr, vals in six.iteritems(state):
            status['mentioned_attributes'].add(attr)
            vals = _toset(vals)
            if directive == 'default':
                if len(vals) and (attr not in entry or not len(entry[attr])):
                    entry[attr] = vals
            elif directive == 'add':
                vals.update(entry.get(attr, ()))
                if len(vals):
                    entry[attr] = vals
            elif directive == 'delete':
                existing_vals = entry.pop(attr, OrderedSet())
                if len(vals):
                    existing_vals -= vals
                    if len(existing_vals):
                        entry[attr] = existing_vals
            elif directive == 'replace':
                entry.pop(attr, None)
                if len(vals):
                    entry[attr] = vals
            else:
                raise ValueError('unknown directive: ' + directive)
コード例 #13
0
 def _test_helper(self,
                  init_db,
                  expected_ret,
                  replace,
                  delete_others=False):
     _init_db(copy.deepcopy(init_db))
     old = _dump_db()
     new = _dump_db()
     expected_db = copy.deepcopy(init_db)
     for dn, attrs in six.iteritems(replace):
         for attr, vals in six.iteritems(attrs):
             if len(vals):
                 new.setdefault(dn, {})[attr] = list(OrderedSet(vals))
                 expected_db.setdefault(dn, {})[attr] = OrderedSet(vals)
             elif dn in expected_db:
                 new[dn].pop(attr, None)
                 expected_db[dn].pop(attr, None)
         if not len(expected_db.get(dn, {})):
             new.pop(dn, None)
             expected_db.pop(dn, None)
     if delete_others:
         dn_to_delete = OrderedSet()
         for dn, attrs in six.iteritems(expected_db):
             if dn in replace:
                 to_delete = OrderedSet()
                 for attr, vals in six.iteritems(attrs):
                     if attr not in replace[dn]:
                         to_delete.add(attr)
                 for attr in to_delete:
                     del attrs[attr]
                     del new[dn][attr]
                 if not len(attrs):
                     dn_to_delete.add(dn)
         for dn in dn_to_delete:
             del new[dn]
             del expected_db[dn]
     name = 'ldapi:///'
     expected_ret['name'] = name
     expected_ret.setdefault('result', True)
     expected_ret.setdefault('comment', 'Successfully updated LDAP entries')
     expected_ret.setdefault(
         'changes',
         dict(((dn, {
             'old':
             dict((attr, vals) for attr, vals in six.iteritems(old[dn])
                  if vals != new.get(dn, {}).get(attr, ()))
             if dn in old else None,
             'new':
             dict((attr, vals) for attr, vals in six.iteritems(new[dn])
                  if vals != old.get(dn, {}).get(attr, ()))
             if dn in new else None
         }) for dn in replace if old.get(dn, {}) != new.get(dn, {}))))
     entries = [{
         dn: [{
             'replace': attrs
         }, {
             'delete_others': delete_others
         }]
     } for dn, attrs in six.iteritems(replace)]
     actual = salt.states.ldap.managed(name, entries)
     self.assertDictEqual(expected_ret, actual)
     self.assertDictEqual(expected_db, db)
コード例 #14
0
ファイル: ldap.py プロジェクト: mamh-mixed/saltstack-salt
def _process_entries(l, entries):
    """Helper for managed() to process entries and return before/after views

    Collect the current database state and update it according to the
    data in :py:func:`managed`'s ``entries`` parameter.  Return the
    current database state and what it will look like after
    modification.

    :param l:
        the LDAP connection object

    :param entries:
        the same object passed to the ``entries`` parameter of
        :py:func:`manage`

    :return:
        an ``(old, new)`` tuple that describes the current state of
        the entries and what they will look like after modification.
        Each item in the tuple is an OrderedDict that maps an entry DN
        to another dict that maps an attribute name to a set of its
        values (it's a set because according to the LDAP spec,
        attribute value ordering is unspecified and there can't be
        duplicates).  The structure looks like this:

            {dn1: {attr1: set([val1])},
             dn2: {attr1: set([val2]), attr2: set([val3, val4])}}

        All of an entry's attributes and values will be included, even
        if they will not be modified.  If an entry mentioned in the
        entries variable doesn't yet exist in the database, the DN in
        ``old`` will be mapped to an empty dict.  If an entry in the
        database will be deleted, the DN in ``new`` will be mapped to
        an empty dict.  All value sets are non-empty:  An attribute
        that will be added to an entry is not included in ``old``, and
        an attribute that will be deleted frm an entry is not included
        in ``new``.

        These are OrderedDicts to ensure that the user-supplied
        entries are processed in the user-specified order (in case
        there are dependencies, such as ACL rules specified in an
        early entry that make it possible to modify a later entry).
    """

    old = OrderedDict()
    new = OrderedDict()

    for entries_dict in entries:
        for dn, directives_seq in entries_dict.items():
            # get the old entry's state.  first check to see if we've
            # previously processed the entry.
            olde = new.get(dn, None)
            if olde is None:
                # next check the database
                results = __salt__["ldap3.search"](l, dn, "base")
                if len(results) == 1:
                    attrs = results[dn]
                    olde = {
                        attr: OrderedSet(attrs[attr])
                        for attr in attrs if len(attrs[attr])
                    }
                else:
                    # nothing, so it must be a brand new entry
                    assert len(results) == 0
                    olde = {}
                old[dn] = olde
            # copy the old entry to create the new (don't do a simple
            # assignment or else modifications to newe will affect
            # olde)
            newe = copy.deepcopy(olde)
            new[dn] = newe

            # process the directives
            entry_status = {
                "delete_others": False,
                "mentioned_attributes": set(),
            }
            for directives in directives_seq:
                _update_entry(newe, entry_status, directives)
            if entry_status["delete_others"]:
                to_delete = set()
                for attr in newe:
                    if attr not in entry_status["mentioned_attributes"]:
                        to_delete.add(attr)
                for attr in to_delete:
                    del newe[attr]
    return old, new
コード例 #15
0
 def _test_helper(self, init_db, expected_ret, replace, delete_others=False):
     _init_db(copy.deepcopy(init_db))
     old = _dump_db()
     new = _dump_db()
     expected_db = copy.deepcopy(init_db)
     for dn, attrs in replace.items():
         for attr, vals in attrs.items():
             vals = [to_bytes(val) for val in vals]
             if vals:
                 new.setdefault(dn, {})[attr] = list(OrderedSet(vals))
                 expected_db.setdefault(dn, {})[attr] = OrderedSet(vals)
             elif dn in expected_db:
                 new[dn].pop(attr, None)
                 expected_db[dn].pop(attr, None)
         if not expected_db.get(dn, {}):
             new.pop(dn, None)
             expected_db.pop(dn, None)
     if delete_others:
         dn_to_delete = OrderedSet()
         for dn, attrs in expected_db.items():
             if dn in replace:
                 to_delete = OrderedSet()
                 for attr, vals in attrs.items():
                     if attr not in replace[dn]:
                         to_delete.add(attr)
                 for attr in to_delete:
                     del attrs[attr]
                     del new[dn][attr]
                 if not attrs:
                     dn_to_delete.add(dn)
         for dn in dn_to_delete:
             del new[dn]
             del expected_db[dn]
     name = "ldapi:///"
     expected_ret["name"] = name
     expected_ret.setdefault("result", True)
     expected_ret.setdefault("comment", "Successfully updated LDAP entries")
     expected_ret.setdefault(
         "changes",
         {
             dn: {
                 "old": {
                     attr: vals
                     for attr, vals in old[dn].items()
                     if vals != new.get(dn, {}).get(attr, ())
                 }
                 if dn in old
                 else None,
                 "new": {
                     attr: vals
                     for attr, vals in new[dn].items()
                     if vals != old.get(dn, {}).get(attr, ())
                 }
                 if dn in new
                 else None,
             }
             for dn in replace
             if old.get(dn, {}) != new.get(dn, {})
         },
     )
     entries = [
         {dn: [{"replace": attrs}, {"delete_others": delete_others}]}
         for dn, attrs in replace.items()
     ]
     actual = salt.states.ldap.managed(name, entries)
     self.assertDictEqual(expected_ret, actual)
     self.assertDictEqual(expected_db, db)
コード例 #16
0
def _test_helper_add(db, expected_ret, add_items, delete_others=False):
    old = db.dump_db()
    new = db.dump_db()
    expected_db = copy.deepcopy(db.db)
    for dn, attrs in add_items.items():
        for attr, vals in attrs.items():
            vals = [to_bytes(val) for val in vals]

            vals.extend(old.get(dn, {}).get(attr, OrderedSet()))
            vals.sort()

            if vals:
                new.setdefault(dn, {})[attr] = list(OrderedSet(vals))
                expected_db.setdefault(dn, {})[attr] = OrderedSet(vals)
            elif dn in expected_db:
                new[dn].pop(attr, None)
                expected_db[dn].pop(attr, None)
        if not expected_db.get(dn, {}):
            new.pop(dn, None)
            expected_db.pop(dn, None)
    if delete_others:
        dn_to_delete = OrderedSet()
        for dn, attrs in expected_db.items():
            if dn in add_items:
                to_delete = OrderedSet()
                for attr, vals in attrs.items():
                    if attr not in add_items[dn]:
                        to_delete.add(attr)
                for attr in to_delete:
                    del attrs[attr]
                    del new[dn][attr]
                if not attrs:
                    dn_to_delete.add(dn)
        for dn in dn_to_delete:
            del new[dn]
            del expected_db[dn]
    name = "ldapi:///"
    expected_ret["name"] = name
    expected_ret.setdefault("result", True)
    expected_ret.setdefault("comment", "Successfully updated LDAP entries")
    expected_ret.setdefault(
        "changes",
        {
            dn: {
                "old": {
                    attr: vals
                    for attr, vals in old[dn].items()
                    if vals != new.get(dn, {}).get(attr, ())
                } if dn in old else None,
                "new": {
                    attr: vals
                    for attr, vals in new[dn].items()
                    if vals != old.get(dn, {}).get(attr, ())
                } if dn in new else None,
            }
            for dn in add_items if old.get(dn, {}) != new.get(dn, {})
        },
    )
    entries = [{
        dn: [{
            "add": attrs
        }, {
            "delete_others": delete_others
        }]
    } for dn, attrs in add_items.items()]
    actual = salt.states.ldap.managed(name, entries)
    assert expected_ret == actual
    assert expected_db == db.db