summaryrefslogtreecommitdiffstats
path: root/lib/openldap
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2013-12-08 06:33:58 +0100
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:51:16 +0200
commit185cf14065554038820c696e7d35f47017b43783 (patch)
treeffbd2d4cdb8760a93b98acb4631dc6c116c21480 /lib/openldap
parent65586b40e5f8eb50d3cba27051dfc57e504b632f (diff)
bugfix
Diffstat (limited to 'lib/openldap')
-rw-r--r--lib/openldap14
1 files changed, 10 insertions, 4 deletions
diff --git a/lib/openldap b/lib/openldap
index 2cc55db..0a8df96 100644
--- a/lib/openldap
+++ b/lib/openldap
@@ -19,40 +19,41 @@
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, pwd
import tempfile, atexit
# 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'll add
# explicit indices to what we find in the LDIF.
indexedAttributes = frozenset([
'olcAttributeTypes',
'olcObjectClasses',
'olcAccess',
'olcSyncrepl',
'olcOverlay',
+ 'olcLimits',
])
# 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' )],
'olcOverlayConfig': [('olcOverlay', '%s' )],
}
# Allow for flexible ACLs for user using SASL's EXTERNAL mechanism.
# "username=postfix,cn=peercred,cn=external,cn=auth" is replaced by
# "gidNumber=106+uidNumber=102,cn=peercred,cn=external,cn=auth" where
# 102 is postfix's UID and 106 its primary GID.
# (Regular expressions are not allowed.)
@@ -147,40 +148,45 @@ def flexibleSearch(module, l, dn, entry):
f = '(&(' + ')('.join(f) + '))'
r = l.search_s( base, scope, filterstr=f )
if len(r) > 1:
module.fail_json(msg="Multiple results found! This is a bug. Please report.")
elif r:
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)
+ if 'olcAccess' in entry.keys():
+ # replace "username=...,cn=peercred,cn=external,cn=auth"
+ # by a DN with proper gidNumber and uidNumber
+ entry['olcAccess'] = map ( partial(sasl_ext_re.sub, acl_sasl_ext)
+ , entry['olcAccess'] )
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 not in entry.keys():
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:
if a == 'olcAccess':
# replace "username=...,cn=peercred,cn=external,cn=auth"
# by a DN with proper gidNumber and uidNumber
entry[a] = map ( partial(sasl_ext_re.sub, acl_sasl_ext)
, entry[a] )
# add explicit indices in the entry from the LDIF
entry[a] = map( (lambda x: '{%d}%s' % x)
, zip(range(len(entry[a])),entry[a]) )
@@ -216,61 +222,61 @@ def processEntry(module, l, dn, entry):
# Load the given module.
def loadModule(module, l, name):
changed = False
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
if module.check_mode:
module.exit_json(changed=changed, msg="add module %s" % name)
l.modify_s( 'cn=module{0},cn=config'
, [(ldap.MOD_ADD, 'olcModuleLoad', name)] )
return changed
# Find the database associated with a given attribute (eg,
# olcDbDirectory or olcSuffix).
-def getDN_DB(module, l, a, v):
+def getDN_DB(module, l, a, v, attrlist=['']):
f = filter_format( '(&(objectClass=olcDatabaseConfig)('+a+'=%s))', [v] )
return l.search_s( 'cn=config'
, ldap.SCOPE_ONELEVEL
, filterstr = f
- , attrlist = [''] )
+ , attrlist = attrlist )
# Clear the given DB directory and delete the associated database. Fail
# if non empty, unless all existing DNS are in skipdns.
def wontRemove(module, skipdns, d, _):
if d not in skipdns:
module.fail_json(msg="won't remove '%s'" % d)
def removeDB(module, dbdir, skipdn=None):
changed = False
if not os.path.exists(dbdir):
return False
l = ldap.initialize( 'ldapi://' )
l.sasl_interactive_bind_s('', ldap.sasl.external())
- r = getDN_DB( module, l, 'olcDbDirectory', dbdir )
+ r = getDN_DB( module, l, 'olcDbDirectory', dbdir, attrlist=['olcSuffix'] )
l.unbind_s()
if len(r) > 1:
module.fail_json(msg="Multiple results found! This is a bug. Please report.")
elif r:
dn,entry = r.pop()
suffix = entry['olcSuffix'][0]
skipdns = [suffix]
if skipdn is not None:
skipdns.extend([ "%s,%s" % (s,suffix) for s in skipdn ])
# here we need to use slapcat not search_s, because we may
# not have read access on the database (even though we're
# root!).
p = slapcat( suffix=suffix )
parser = LDIFCallback( module, p.stdout
, partial(wontRemove,module,skipdns) )
parser.parse()
changed = True
@@ -351,41 +357,41 @@ def main():
mod = params['module']
suffix = params['suffix']
form = params['format']
name = params['name']
if ignoredn is not None:
ignoredn = ignoredn.split(':')
changed = False
try:
if state == "absent":
if dbdirectory is not None:
changed = removeDB(module,dbdirectory,skipdn=ignoredn)
# TODO: might be useful to be able remove DNs
else:
module.fail_json(msg="missing dbdirectory")
elif state == "present":
if form == 'slapd.conf':
if name is None:
- module.fail_json(msg="name")
+ module.fail_json(msg="missing name")
target = slapd_to_ldif(target, name)
if target is None and mod is None:
module.fail_json(msg="missing target or module")
# bind only once per LDIF file for performance
l = ldap.initialize( 'ldapi://' )
l.sasl_interactive_bind_s('', ldap.sasl.external())
if mod is None:
callback = partial(processEntry,module,l)
else:
changed |= loadModule (module, l, '%s.la' % mod)
if target is None and suffix is None:
l.unbind_s()
module.exit_json(changed=changed)
if target is None or suffix is None:
module.fail_json(msg="missing target or suffix")
r = getDN_DB(module, l, 'olcSuffix', suffix)
if not r:
module.fail_json(msg="No database found for suffix %s" % suffix)