| |
@@ -1,5 +1,5 @@
|
| |
# --- BEGIN COPYRIGHT BLOCK ---
|
| |
- # Copyright (C) 2016 Red Hat, Inc.
|
| |
+ # Copyright (C) 2019 Red Hat, Inc.
|
| |
# Copyright (C) 2019 William Brown <william@blackhats.net.au>
|
| |
# All rights reserved.
|
| |
#
|
| |
@@ -129,7 +129,9 @@
|
| |
:returns: Entry object
|
| |
"""
|
| |
|
| |
- return self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=["*"], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ return self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=["*"],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
|
| |
+ escapehatch='i am sure')[0]
|
| |
|
| |
def exists(self):
|
| |
"""Check if the entry exists
|
| |
@@ -138,19 +140,22 @@
|
| |
"""
|
| |
|
| |
try:
|
| |
- self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrsonly=1, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
+ self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrsonly=1,
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
|
| |
+ escapehatch='i am sure')
|
| |
except ldap.NO_SUCH_OBJECT:
|
| |
return False
|
| |
|
| |
return True
|
| |
|
| |
- def display(self):
|
| |
+ def display(self, attrlist=['*']):
|
| |
"""Get an entry but represent it as a string LDIF
|
| |
|
| |
:returns: LDIF formatted string
|
| |
"""
|
| |
-
|
| |
- e = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=["*"], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ e = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=attrlist,
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
|
| |
+ escapehatch='i am sure')[0]
|
| |
return e.__repr__()
|
| |
|
| |
def display_attr(self, attr):
|
| |
@@ -227,7 +232,9 @@
|
| |
raise ValueError("Invalid state. Cannot get presence on instance that is not ONLINE")
|
| |
self._log.debug("%s present(%r) %s" % (self._dn, attr, value))
|
| |
|
| |
- e = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=[attr, ], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ e = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=[attr, ],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
|
| |
+ escapehatch='i am sure')[0]
|
| |
values = self.get_attr_vals_bytes(attr)
|
| |
self._log.debug("%s contains %s" % (self._dn, values))
|
| |
|
| |
@@ -280,7 +287,8 @@
|
| |
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')
|
| |
+ return self._instance.modify_ext_s(self._dn, mods, serverctrls=self._server_controls,
|
| |
+ clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
|
| |
# This needs to work on key + val, and key
|
| |
def remove(self, key, value):
|
| |
@@ -375,7 +383,8 @@
|
| |
value = [ensure_bytes(value)]
|
| |
|
| |
return self._instance.modify_ext_s(self._dn, [(action, key, value)],
|
| |
- serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
+ 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
|
| |
@@ -415,7 +424,7 @@
|
| |
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 _unsafe_compare_attribute(self, attr, values):
|
| |
+ def _unsafe_compare_attribute(self, other):
|
| |
"""Compare two attributes from two objects. This is currently marked unsafe as it's
|
| |
not complete yet.
|
| |
|
| |
@@ -430,12 +439,7 @@
|
| |
|
| |
To allow schema aware checking, we need to call ldap compare extop here.
|
| |
"""
|
| |
- return all([
|
| |
- self._instance.compare_ext_s(self._dn, attr, value, serverctrls=self._server_controls,
|
| |
- clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
- for value in values
|
| |
- ])
|
| |
-
|
| |
+ pass
|
| |
|
| |
@classmethod
|
| |
def compare(cls, obj1, obj2):
|
| |
@@ -474,14 +478,10 @@
|
| |
if set(obj1_attrs.keys()) != set(obj2_attrs.keys()):
|
| |
obj1._log.debug("%s != %s" % (obj1_attrs.keys(), obj2_attrs.keys()))
|
| |
return False
|
| |
- obj1._log.debug(sorted(obj1_attrs.keys()))
|
| |
# Check the values of each key
|
| |
# using obj1_attrs.keys() because obj1_attrs.iterkleys() is not supported in python3
|
| |
for key in obj1_attrs.keys():
|
| |
- # Check if they are offline/online?
|
| |
- # if set(obj1_attrs[key]) != set(obj2_attrs[key]):
|
| |
- obj1._log.debug("checking %s: %s ..." % (key, obj2_attrs[key]))
|
| |
- if not obj1._unsafe_compare_attribute(key, obj2_attrs[key]):
|
| |
+ if set(obj1_attrs[key]) != set(obj2_attrs[key]):
|
| |
obj1._log.debug(" v-- %s != %s" % (key, key))
|
| |
obj1._log.debug("%s != %s" % (obj1_attrs[key], obj2_attrs[key]))
|
| |
return False
|
| |
@@ -503,7 +503,7 @@
|
| |
cx = [x.lower() for x in self._compare_exclude]
|
| |
compare_attrs = set(all_attrs_lower.keys()) - set(cx)
|
| |
|
| |
- compare_attrs_dict = {attr.lower():all_attrs_lower[attr] for attr in compare_attrs}
|
| |
+ compare_attrs_dict = {attr.lower(): all_attrs_lower[attr] for attr in compare_attrs}
|
| |
|
| |
return compare_attrs_dict
|
| |
|
| |
@@ -518,7 +518,9 @@
|
| |
raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
else:
|
| |
# retrieving real(*) and operational attributes(+)
|
| |
- attrs_entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=["*", "+"], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ attrs_entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter,
|
| |
+ attrlist=["*", "+"], serverctrls=self._server_controls,
|
| |
+ clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
# getting dict from 'entry' object
|
| |
attrs_dict = attrs_entry.data
|
| |
# Should we normalise the attr names here to lower()?
|
| |
@@ -530,14 +532,18 @@
|
| |
if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
else:
|
| |
- entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=keys, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter,
|
| |
+ attrlist=keys, serverctrls=self._server_controls,
|
| |
+ clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
return entry.getValuesSet(keys)
|
| |
|
| |
def get_attrs_vals_utf8(self, keys, use_json=False):
|
| |
self._log.debug("%s get_attrs_vals_utf8(%r)" % (self._dn, keys))
|
| |
if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
- entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=keys, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=keys,
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
|
| |
+ escapehatch='i am sure')[0]
|
| |
vset = entry.getValuesSet(keys)
|
| |
r = {}
|
| |
for (k, vo) in vset.items():
|
| |
@@ -554,7 +560,9 @@
|
| |
else:
|
| |
# It would be good to prevent the entry code intercepting this ....
|
| |
# We have to do this in this method, because else we ignore the scope base.
|
| |
- entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=[key], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter,
|
| |
+ attrlist=[key], serverctrls=self._server_controls,
|
| |
+ clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
vals = entry.getValues(key)
|
| |
if use_json:
|
| |
result = {key: []}
|
| |
@@ -572,7 +580,9 @@
|
| |
# In the future, I plan to add a mode where if local == true, we
|
| |
# can use get on dse.ldif to get values offline.
|
| |
else:
|
| |
- entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=[key], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
+ entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter,
|
| |
+ attrlist=[key], serverctrls=self._server_controls,
|
| |
+ clientctrls=self._client_controls, escapehatch='i am sure')[0]
|
| |
return entry.getValue(key)
|
| |
|
| |
def get_attr_val_bytes(self, key, use_json=False):
|
| |
@@ -679,7 +689,7 @@
|
| |
pass
|
| |
|
| |
# Modifies the DN of an entry to the new fqdn provided
|
| |
- def rename(self, new_rdn, newsuperior=None):
|
| |
+ def rename(self, new_rdn, newsuperior=None, deloldrdn=True):
|
| |
"""Renames the object within the tree.
|
| |
|
| |
If you provide a newsuperior, this will move the object in the tree.
|
| |
@@ -700,9 +710,12 @@
|
| |
# and the superior as the base (if it changed)
|
| |
if self._protected:
|
| |
return
|
| |
- self._instance.rename_s(self._dn, new_rdn, newsuperior, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
+
|
| |
+ self._instance.rename_s(self._dn, new_rdn, newsuperior,
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
|
| |
+ delold=deloldrdn, escapehatch='i am sure')
|
| |
search_base = self._basedn
|
| |
- if newsuperior != None:
|
| |
+ if newsuperior is not None:
|
| |
# Well, the new DN should be rdn + newsuperior.
|
| |
self._dn = '%s,%s' % (new_rdn, newsuperior)
|
| |
else:
|
| |
@@ -714,7 +727,7 @@
|
| |
|
| |
# assert we actually got the change right ....
|
| |
|
| |
- def delete(self):
|
| |
+ def delete(self, recursive=False):
|
| |
"""Deletes the object defined by self._dn.
|
| |
This can be changed with the self._protected flag!
|
| |
"""
|
| |
@@ -722,7 +735,13 @@
|
| |
self._log.debug("%s delete" % (self._dn))
|
| |
if not self._protected:
|
| |
# Is there a way to mark this as offline and kill it
|
| |
- self._instance.delete_ext_s(self._dn, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
+ if recursive:
|
| |
+ filterstr = "(|(objectclass=*)(objectclass=ldapsubentry))"
|
| |
+ ents = self._instance.search_s(self._dn, ldap.SCOPE_SUBTREE, filterstr, escapehatch='i am sure')
|
| |
+ for ent in sorted(ents, key=lambda e: len(e.dn), reverse=True):
|
| |
+ self._instance.delete_ext_s(ent.dn, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
+ else:
|
| |
+ self._instance.delete_ext_s(self._dn, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
|
| |
|
| |
def _validate(self, rdn, properties, basedn):
|
| |
"""Used to validate a create request.
|
| |
@@ -1121,7 +1140,7 @@
|
| |
|
| |
def filter(self, search):
|
| |
# This will yield and & filter for objectClass with as many terms as needed.
|
| |
- search_filter = _gen_and([self._get_objectclass_filter(),search])
|
| |
+ search_filter = _gen_and([self._get_objectclass_filter(), search])
|
| |
self._log.debug('list filter = %s' % search_filter)
|
| |
try:
|
| |
results = self._instance.search_ext_s(
|
| |
@@ -1137,5 +1156,3 @@
|
| |
# There are no objects to select from, se we return an empty array
|
| |
insts = []
|
| |
return insts
|
| |
-
|
| |
-
|
| |
Description:
Added Conflict Entry and Glue entry classes to lib389, and updated dsconf to allow for conflict entry management.
Made some other minor changes to mapped objects:
https://pagure.io/389-ds-base/issue/50327