summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2014-07-07 23:02:45 +0200
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:52:41 +0200
commit9198e7f8096e9f1b0d5f474cf2345913a357f864 (patch)
tree940cafc428e311b8ea82d9dad7a59c8bfb9251ac
parent3e38718677b10faca8970d9b1cc8edc215cce798 (diff)
Make the Ansible LDAP plugin able to delete entries and attributes.
Use it to delete cn=admin,dc=fripost,dc=org, and to remove the rootDN on the 'config' database.
-rw-r--r--lib/action_plugins/openldap.py3
-rw-r--r--lib/modules/openldap37
-rw-r--r--roles/LDAP-provider/tasks/main.yml2
-rw-r--r--roles/common-LDAP/tasks/main.yml14
4 files changed, 47 insertions, 9 deletions
diff --git a/lib/action_plugins/openldap.py b/lib/action_plugins/openldap.py
index ee8a991..5dbf59f 100644
--- a/lib/action_plugins/openldap.py
+++ b/lib/action_plugins/openldap.py
@@ -14,40 +14,43 @@
# 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 os
import pipes
import tempfile
from ansible.utils import template
from ansible import utils
from ansible.runner.return_data import ReturnData
class ActionModule(object):
TRANSFERS_FILES = True
def __init__(self, runner):
self.runner = runner
def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs):
''' handler for file transfer operations '''
+ if self.runner.noop_on_check(inject):
+ return ReturnData(conn=conn, comm_ok=True, result=dict(skipped=True))
+
# load up options
options = {}
if complex_args:
options.update(complex_args)
options.update(utils.parse_kv(module_args))
target = options.get('target', None)
local = options.get('local', 'no')
if local not in [ 'no', 'file', 'template' ]:
result = dict(failed=True, msg="local must be in ['no','file','template']")
return ReturnData(conn=conn, comm_ok=False, result=result)
if local != 'no' and target is None:
result = dict(failed=True, msg="target is required in local mode")
return ReturnData(conn=conn, comm_ok=False, result=result)
if local == 'no':
# run the module remotely
return self.runner._execute_module(conn, tmp, 'openldap', module_args, inject=inject, complex_args=complex_args)
diff --git a/lib/modules/openldap b/lib/modules/openldap
index 1e84c32..69ee4df 100644
--- a/lib/modules/openldap
+++ b/lib/modules/openldap
@@ -248,65 +248,92 @@ def slapd_to_ldif(src, name):
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(
- 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"] ),
+ delete = dict( default=None ),
),
supports_check_mode=True
)
params = module.params
- state = params['state']
target = params['target']
mod = params['module']
suffix = params['suffix']
form = params['format']
name = params['name']
+ delete = params['delete']
changed = False
try:
- if state == "absent":
- module.fail_json(msg="OpenLDAP's ansible: unsupported feature")
+ if delete is not None:
+ if name is None:
+ module.fail_json(msg="missing name")
+ l = ldap.initialize( 'ldapi://' )
+ l.sasl_interactive_bind_s('', ldap.sasl.external())
+ if delete == 'entry':
+ filterStr = '(objectClass=*)'
+ else:
+ filterStr = [ '(%s=*)' % x for x in delete.split(',') ]
+ if len(filterStr) > 1:
+ filterStr = '(|' + ''.join(filterStr) + ')'
+ else:
+ filterStr = filterStr[0]
+
+ try:
+ r = l.search_s( name, ldap.SCOPE_BASE, filterStr, attrsonly=1 )
+ except ldap.LDAPError, ldap.NO_SUCH_OBJECT:
+ r = None
- elif state == "present":
+ if r:
+ changed = True
+ if module.check_mode:
+ module.exit_json(changed=changed)
+ if delete == 'entry':
+ l.delete_s(r[0][0])
+ else:
+ attrlist = list(set(r[0][1].keys()) & set(delete.split(',')))
+ l.modify_s(r[0][0], [ (ldap.MOD_DELETE, x, None) for x in attrlist ])
+ l.unbind_s()
+
+ else:
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)
if target is None or suffix is None:
module.fail_json(msg="missing target or suffix")
diff --git a/roles/LDAP-provider/tasks/main.yml b/roles/LDAP-provider/tasks/main.yml
index 0ba4f26..fa212a0 100644
--- a/roles/LDAP-provider/tasks/main.yml
+++ b/roles/LDAP-provider/tasks/main.yml
@@ -1,14 +1,14 @@
- name: Load and configure the syncprov overlay
- openldap: module=syncprov state=present
+ openldap: module=syncprov
suffix=dc=fripost,dc=org
target=etc/ldap/syncprov.ldif
local=file
- name: Enable the EXTERNAL SASL mechanism
lineinfile: dest=/usr/lib/sasl2/slapd.conf
regexp='^mech_list'':'
line=mech_list':'' EXTERNAL'
owner=root group=root
mode=0644
# TODO: authz constraint
diff --git a/roles/common-LDAP/tasks/main.yml b/roles/common-LDAP/tasks/main.yml
index 85ad831..e86fa45 100644
--- a/roles/common-LDAP/tasks/main.yml
+++ b/roles/common-LDAP/tasks/main.yml
@@ -95,34 +95,42 @@
service: name=slapd state=started
when: not (r1.changed or r2.changed)
- meta: flush_handlers
- name: Copy fripost & amavis' schema
copy: src=etc/ldap/schema/{{ item }}
dest=/etc/ldap/schema/{{ item }}
owner=root group=root
mode=0644
# It'd certainly be nicer if we didn't have to deploy amavis' schema
# everywhere, but we need the 'objectClass' in our replicates, hence
# they need to be aware of the 'amavisAccount' class.
with_items:
- fripost.ldif
- amavis.schema
tags:
- amavis
- name: Load amavis' schema
- openldap: target=/etc/ldap/schema/amavis.schema state=present
+ openldap: target=/etc/ldap/schema/amavis.schema
format=slapd.conf name=amavis
tags:
- ldap
- name: Load Fripost' schema
- openldap: target=/etc/ldap/schema/fripost.ldif state=present
+ openldap: target=/etc/ldap/schema/fripost.ldif
tags:
- ldap
# We assume a clean (=stock) cn=config
- name: Configure the LDAP database
openldap: target=etc/ldap/database.ldif.j2 local=template
- state=present
+
+# On read-only replicates, you might have to temporarily switch back to
+# read-write, delete the SyncRepl, and delete the DN manually:
+# sudo ldapdelete -Y EXTERNAL -H ldapi:// cn=admin,dc=fripost,dc=org
+- name: Remove cn=admin,dc=fripost,dc=org
+ openldap: name="cn=admin,dc=fripost,dc=org" delete=entry
+
+- name: Remove the rootDN under the 'config' database
+ openldap: name="olcDatabase={0}config,cn=config" delete=olcRootDN,olcRootPW