diff options
Diffstat (limited to 'lib/openldap')
-rw-r--r-- | lib/openldap | 51 |
1 files changed, 30 insertions, 21 deletions
diff --git a/lib/openldap b/lib/openldap index 39ccd8e..5a4cef4 100644 --- a/lib/openldap +++ b/lib/openldap @@ -10,44 +10,42 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import ldap, ldap.sasl from ldap.filter import filter_format from ldap.dn import dn2str,explode_dn,str2dn from ldap.modlist import addModlist from ldif import LDIFParser from functools import partial import re # Dirty hack to check equality between the targetted LDIF and that # currently in the directory. The value of some configuration (olc*) -# attributes is automatically indexed when added; for those we remove -# the index before checking the equality between the two lists of -# values. -idxAttr_re = re.compile( '^\{\d+\}(.*)' ) +# attributes is automatically indexed when added; for those we'll add +# explicit indices to what we find in the LDIF. indexedAttributes = frozenset([ 'olcAttributeTypes', 'olcObjectClasses', 'olcAccess', ]) # Another hack. Configuration entries sometimes pollutes the DNs with # indices, thus it's not possible to directly use them as base. # Instead, we use their parent as a pase, and search for the *unique* # match with the same ObjectClass and the matching extra attributes. # ('%s' in the attribute value is replaced with the value of the source # entry.) indexedDN = { 'olcSchemaConfig': [('cn', '{*}%s')], 'olcHdbConfig': [('olcDbDirectory', '%s' )], } # Run the given callback on each DN seen. If its return value is not @@ -128,59 +126,70 @@ def flexibleSearch(module, l, dn, entry): return r.pop() # Add or modify (only the attributes that differ from those in the # directory) the entry for that DN. # l must be an LDAPObject, and should provide an open connection to the # directory with disclose/search/write access. def processEntry(module, l, dn, entry): changed = False r = flexibleSearch( module, l, dn, entry ) if r is None: changed = True if module.check_mode: module.exit_json(changed=changed, msg="add DN %s" % dn) l.add_s( dn, addModlist(entry) ) else: d,e = r fst = str2dn(dn).pop(0)[0][0] diff = [] for a,v in e.iteritems(): - if a == fst: - # the first attribute of the DN is implicit - continue if a not in entry.keys(): - diff.append((ldap.MOD_DELETE, a, v)) - continue - if a in indexedAttributes: - # remove indices - v = [ idxAttr_re.search(v1).group(1) for v1 in v ] - if v != entry[a]: - # TODO: finer grain: we should modify/add/delete - # based on couple (attr,value), not attr only. - # The difficulty is that we need to preserve the order - # if a in indexedAttributes. Maybe we should index - # entry[a] instead, and walk on both lists at once? - diff.append((ldap.MOD_REPLACE, a, entry[a])) - + if a != fst: + # delete all values except for the first attribute, + # which is implicit + diff.append(( ldap.MOD_DELETE, a, None )) + elif a in indexedAttributes: + # add explicit indices in the entry from the LDIF + entry[a] = map( (lambda x: '{%d}%s' % x) + , zip(range(len(entry[a])),entry[a]) ) + if v != entry[a]: + diff.append(( ldap.MOD_REPLACE, a, entry[a] )) + elif v != entry[a]: + # for non-indexed attribute, we update values in the + # symmetric difference only + s1 = set(v) + s2 = set(entry[a]) + if s1.isdisjoint(s2): + # replace the former values with the new ones + diff.append(( ldap.MOD_REPLACE, a, entry[a] )) + else: + x = list(s1.difference(s2)) + if x: + diff.append(( ldap.MOD_DELETE, a, x )) + y = list(s2.difference(s1)) + if y: + diff.append(( ldap.MOD_ADD, a, y )) + + # add attributes that weren't in e for a in set(entry).difference(e.keys()): - diff.append((ldap.MOD_ADD, a, entry[a])) + diff.append(( ldap.MOD_ADD, a, entry[a] )) if diff: changed = True if module.check_mode: module.exit_json(changed=changed, msg="mod DN %s" % dn) l.modify_s( d, diff ) return changed # Load the given module. def loadModule(module, name): changed = False l = ldap.initialize( 'ldapi://' ) l.sasl_interactive_bind_s('', ldap.sasl.external()) f = filter_format( '(&(objectClass=olcModuleList)(olcModuleLoad=%s))', [name] ) r = l.search_s( 'cn=config', ldap.SCOPE_ONELEVEL, filterstr = f, attrlist = [] ) if not r: changed = True |