summaryrefslogtreecommitdiffstats
path: root/lib/modules
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2014-07-07 18:37:30 +0200
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:52:39 +0200
commit2dfe29dfcd35fae7160178e329fb0647cc896e3b (patch)
tree87670f8e62e07dceea26a58cf7aeaf0a57fb62af /lib/modules
parentddf4de6593756993f859c020bc6db046ca869846 (diff)
Remove o=mailHosting from the LDAP directory suffix.
So our suffix is now a mere 'dc=fripost,dc=org'. We're also using the default '/var/lib/ldap' as olcDbDirectory (hence we don't clear it before hand).
Diffstat (limited to 'lib/modules')
-rw-r--r--lib/modules/openldap96
1 files changed, 3 insertions, 93 deletions
diff --git a/lib/modules/openldap b/lib/modules/openldap
index 0f0bc9a..1e84c32 100644
--- a/lib/modules/openldap
+++ b/lib/modules/openldap
@@ -21,40 +21,41 @@ 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',
'olcAuthzRegexp',
+ 'olcDbConfig',
])
# 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.)
@@ -74,68 +75,40 @@ def acl_sasl_ext(m):
, pwd_dict[u].pw_gid
, pwd_dict[u].pw_uid
, m.group('end')
)
# Run the given callback on each DN seen. If its return value is not
# None, update the changed variable.
class LDIFCallback(LDIFParser):
def __init__(self, module, input, callback):
LDIFParser.__init__(self,input)
self.callback = callback
self.changed = False
def handle(self,dn,entry):
b = self.callback(dn,entry)
if b is not None:
self.changed |= b
-# Run slapcat(8) on the given suffix or DB number (suffix takes
-# precedence) with an optional filter. (This is useful for offline
-# searches, or one needs to bypass ACLs.) Returns an open pipe to the
-# subprocess.
-def slapcat(filter=None, suffix=None, idx=0):
- cmd = [ os.path.join(os.sep, 'usr', 'sbin', 'slapcat') ]
-
- if filter is not None:
- cmd.extend([ '-a', filter ])
-
- if suffix is not None:
- if type(suffix) is not str:
- suffix = dn2str(suffix)
- cmd.extend([ '-b', suffix ])
- else:
- cmd.append( '-n%d' % idx )
-
- return subprocess.Popen( cmd, stdout=subprocess.PIPE
- , stderr=open(os.devnull, 'wb') )
-
-
-# Start / stop / whatever a service.
-def service(name, state):
- cmd = [ os.path.join(os.sep, 'usr', 'sbin', 'service'), name, state ]
- subprocess.check_call( cmd, stdout=open(os.devnull, 'wb')
- , stderr=subprocess.STDOUT )
-
-
# Check if the given dn is already present in the directory.
# Returns None if doesn't exist, and give the dn,entry otherwise
def flexibleSearch(module, l, dn, entry):
idxClasses = set(entry['objectClass']).intersection(indexedDN.keys())
if not idxClasses:
base = dn
scope = ldap.SCOPE_BASE
f = 'objectClass=*'
else:
# Search on the parent instead, and try to use a precise filter
dn = str2dn(dn)
h,t,_ = dn.pop(0)[0]
base = dn2str(dn)
scope = ldap.SCOPE_ONELEVEL
f = []
for c in idxClasses:
f.append ( filter_format('objectClass=%s', [c]) )
for a,v in indexedDN[c]:
if a == h:
v2 = t
@@ -237,164 +210,101 @@ def loadModule(module, l, name):
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, attrlist=['']):
f = filter_format( '(&(objectClass=olcDatabaseConfig)('+a+'=%s))', [v] )
return l.search_s( 'cn=config'
, ldap.SCOPE_ONELEVEL
, filterstr = f
, 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, 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
- if module.check_mode:
- module.exit_json(changed=changed, msg="remove dir %s" % dbdir)
-
- # slapd doesn't support database deletion, so we need to turn it
- # off and remove it from slapd.d manually.
- service( 'slapd', 'stop' )
- path = [ os.sep, 'etc', 'ldap', 'slapd.d' ]
- ldif = explode_dn(dn)[::-1]
- ldif[-1] += ".ldif"
- path.extend( ldif )
- os.unlink( os.path.join(*path) )
-
- # delete all children in path, but not the path directory itself.
- for file in os.listdir(dbdir):
- os.unlink( os.path.join(dbdir, file) )
- service( 'slapd', 'start' )
- return changed
-
-
# Convert a *.schema file into *.ldif format. The algorithm can be found
# in /etc/ldap/schema/openldap.ldif .
def slapd_to_ldif(src, name):
s = open( src, 'r' )
d = tempfile.NamedTemporaryFile(delete=False)
atexit.register(lambda: os.unlink( d.name ))
d.write('dn: cn=%s,cn=schema,cn=config\n' % name)
d.write('objectClass: olcSchemaConfig\n')
re1 = re.compile( r'^objectIdentifier\s(.*)', re.I )
re2 = re.compile( r'^objectClass\s(.*)', re.I )
re3 = re.compile( r'^attributeType\s(.*)', re.I )
reSp = re.compile( r'^\s+' )
for line in s.readlines():
if line == '\n':
line = '#\n'
m1 = re1.match(line)
m2 = re2.match(line)
m3 = re3.match(line)
if m1 is not None:
line = 'olcObjectIdentifier: %s' % m1.group(1)
elif m2 is not None:
line = 'olcObjectClasses: %s' % m2.group(1)
elif m3 is not None:
line = 'olcAttributeTypes: %s' % m3.group(1)
d.write( reSp.sub(line, ' ') )
s.close()
d.close()
return d.name
def main():
module = AnsibleModule(
argument_spec = dict(
- dbdirectory = dict( default=None ),
- ignoredn = dict( default=None ),
- state = dict( default="present", choices=["absent", "present"]),
+ state = dict( default="present", choices=["absent","present"]),
target = dict( default=None ),
module = dict( default=None ),
suffix = dict( default=None ),
format = dict( default="ldif", choices=["ldif","slapd.conf"] ),
name = dict( default=None ),
local = dict( default="no", choices=["no","file","template"] ),
),
supports_check_mode=True
)
params = module.params
state = params['state']
- dbdirectory = params['dbdirectory']
- ignoredn = params['ignoredn']
target = params['target']
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")
+ module.fail_json(msg="OpenLDAP's ansible: unsupported feature")
elif state == "present":
if form == 'slapd.conf':
if name is None:
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)