summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2013-12-08 04:49:57 +0100
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:51:15 +0200
commit65586b40e5f8eb50d3cba27051dfc57e504b632f (patch)
tree5fd3869ebf7811e10f7d0358caee7065e531a28b /lib
parent698834ee35adbd4b6b95228d27cb515632980d3a (diff)
Convert legacy *.schema into *.ldif.
Diffstat (limited to 'lib')
-rw-r--r--lib/openldap49
1 files changed, 47 insertions, 2 deletions
diff --git a/lib/openldap b/lib/openldap
index 6f2bb68..2cc55db 100644
--- a/lib/openldap
+++ b/lib/openldap
@@ -6,40 +6,41 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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, 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',
])
# 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
@@ -161,41 +162,41 @@ def processEntry(module, l, dn, entry):
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 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(re.sub, sasl_ext_re, acl_sasl_ext)
+ 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]) )
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:
@@ -275,74 +276,118 @@ def removeDB(module, dbdir, skipdn=None):
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 ),
),
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")
elif state == "present":
+ if form == 'slapd.conf':
+ if name is None:
+ module.fail_json(msg="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)
elif len(r) > 1:
module.fail_json(msg="Multiple results found! This is a bug. Please report.")