Changeset - d1a4cfa37a3f
[Not reviewed]
0 0 1
Branko Majic (branko) - 9 years ago 2014-11-21 00:19:36
branko@majic.rs
MAR-1: Added a small module for managing the LDAP permissions.
1 file changed with 202 insertions and 0 deletions:
0 comments (0 inline, 0 general)
roles/ldap_server/library/ldap_permissions
Show inline comments
 
new file 100644
 
#!/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()
0 comments (0 inline, 0 general)