#!/usr/bin/env python DOCUMENTATION = """ --- module: ldap_permissions short_description: Sets permissions/ACL for LDAP database. description: - Sets permissions (access control list) for LDAP database. version_added: 1.7.2 author: Branko Majic notes: - Requires the python-ldap Python package on remote host. For Debian and derivatives, this is as easy as apt-get install python-ldap. requirements: - python-ldap options: filter: description: - LDAP filter that should be used for locating the database on which the ACL rules should be applied. This filter will be used for search under the C(cn=config) base DN. For regular user databases, the filter should probably be based on the C(olcSuffix) attribute. The filter must result in a unique entry. required: true default: "" rules: description: - LDAP rules that should be applied to the LDAP database. The rules should be provided as a list of strings. Each string should be an access rule as described in OpenLDAP administrator guide at U(http://www.openldap.org/doc/admin24/access-control.html). Use long format for specifying this parameter (see examples below). required: true default: "" """ EXAMPLES = """ # Set-up of rules for regular database. ldap_permissions: - filter: '(olcSuffix=dc=example,dc=com)' rules: - > to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break - > to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=example,dc=com" write by * none - > to dn.base="" by * read - > to * by self write by dn="cn=admin,dc=example,dc=com" write by * none # Set-up rules for a configuration database. This time with a single rule in a # single line. ldap_permissions: - filter: '(olcDatabase={0}config)' rules: - to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break """ # Try to load the Python LDAP module. try: import ldap import ldap.sasl import ldap.modlist except ImportError: ldap_found = False else: ldap_found = True class LDAPPermissions(object): """ This class encapsulates functionality for applying ACL to an LDAP database. """ def __init__(self, module): """ Initialises class instance. Reads parameters from the passed AnsibleModule instance, and connects to the LDAP server. """ self.module = module self.filter = module.params["filter"] self.rules = module.params["rules"] self._connect() def _connect(self): """ Initialises LDAP connection and binds to the LDAP server. Binding is done using the SASL EXTERNAL mechanism. Returns: Nothing. """ self.connection = ldap.initialize("ldapi:///") self.connection.sasl_interactive_bind_s("", ldap.sasl.external()) def _get_database(self): """ Retrieves the requested database entry. Returns: Database entry. Return format is same as for function ldap.search_s. """ return self.connection.search_s(base="cn=config", scope=ldap.SCOPE_ONELEVEL, filterstr=self.filter) def _get_modifications(self, database): """ Returns modification list for updatingn the current ACL with requested ACL. Returns: Modification list. The format is suitable for use with functions ldap.modify() and ldap.modify_s(). An empty list will be returned if no changes are necessary. """ # Fetch the list of current rules. current_rules = database[1]["olcAccess"] # Set-up list of requested rules. requested_rules = [] for n, rule in enumerate(self.rules): rule = "{%d}%s" % (n, rule) requested_rules.append(rule.rstrip().lstrip().decode("utf-8").encode("utf-8")) return ldap.modlist.modifyModlist({'olcAccess': current_rules}, {'olcAccess': requested_rules}) def apply(self): """ Applies permissions requested via the Ansible module configuration. The function also produces the necessary JSON output, and terminates module execution as appropriate. Returns: Nothing. """ # Fetch the database config based on filter and verify and only one was # returned. databases = self._get_database() if databases == []: self.module.fail_json(msg="No database matched filter: %s" % self.filter) elif len(databases) > 1: self.module.fail_json(msg="More than one databases matched filter: %s" % self.filter) database = databases[0] # Set-up the modification list. modify_list = self._get_modifications(database) # Apply modifications if necessary. if modify_list == []: self.module.exit_json(changed=False) else: try: self.connection.modify_s(database[0], modify_list) except ldap.OTHER as e: self.module.fail_json(msg="Failed to modify permissions. Rule syntax was possibly incorrect. LDAP server responded: %s" % e) self.module.exit_json(changed=True) def main(): """ Runs the module. """ # Construct the module helper for parsing the arguments. module = AnsibleModule( argument_spec=dict( filter=dict(required=True), rules=dict(required=True), ) ) if not ldap_found: module.fail_json(msg="The Python LDAP module is required") ldap_rules = LDAPPermissions(module) ldap_rules.apply() # Import module snippets. from ansible.module_utils.basic import * main()