def _ldap_utilities_set_password_function(self, event, *args, **kwargs):
        """Function: A function that allows you to set a new password for an LDAP entry given the entry's DN"""
        log = logging.getLogger(__name__)

        try:
            yield StatusMessage("Starting ldap_utilities_set_password")

            # Instansiate helper (which gets appconfigs from file)
            helper = LDAPUtilitiesHelper(self.options)
            yield StatusMessage("Appconfig Settings OK")
            
            # Get function inputs
            input_ldap_dn = helper.get_function_input(kwargs, "ldap_dn") # text (required)
            input_ldap_new_password = helper.get_function_input(kwargs, "ldap_new_password") # text (required)
            yield StatusMessage("Function Inputs OK")

            # Instansiate LDAP Server and Connection
            c = helper.get_ldap_connection()

            try:
              # Bind to the connection
              c.bind()
            except:
              raise ValueError("Cannot connect to LDAP Server. Ensure credentials are correct")
            
            # Inform user
            msg = ""
            if helper.LDAP_IS_ACTIVE_DIRECTORY:
              msg = "Connected to {0}".format("Active Directory")
            else:
              msg = "Connected to {0}".format("LDAP Server")
            yield StatusMessage(msg)

            res = False
            
            try:
              yield StatusMessage("Attempting to change password")
              if helper.LDAP_IS_ACTIVE_DIRECTORY:
                res = c.extend.microsoft.modify_password(str(input_ldap_dn), input_ldap_new_password)
              else:
                res = c.modify(input_ldap_dn, {'userPassword': [(MODIFY_REPLACE, [input_ldap_new_password])]})

            except Exception:
              raise ValueError("Could not change password. Check input_ldap_dn and input_ldap_new_password are valid")

            finally:
              # Unbind connection
              c.unbind()

            results = {
                "success": res,
                "user_dn": input_ldap_dn
            }

            log.info("Completed")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _ldap_utilities_search_function(self, event, *args, **kwargs):
        """Resilient Function: entry point """

        LOG = logging.getLogger(__name__)

        def escape_chars(str, encoding=None):
            """ Escape some characters in filter.

          Escape a set of characters in the filter string to help to mitigate against possibility of injection.
          This has a subset of characters escaped in ldap3 function escape_filter_chars.

          """
            if encoding is None:
                encoding = get_config_parameter('DEFAULT_ENCODING')

            str = to_unicode(str, encoding)
            escaped_str = str.replace('\\', '\\5c')
            escaped_str = escaped_str.replace('*', '\\2a')
            escaped_str = escaped_str.replace('\x00', '\\00')

            return escaped_str

        def validate_params(search_params):
            """"Check mandatory fields

          Do a number of checks on input fields.

          """
            for k in search_params:
                if re.match(r'^search_filter$', k):
                    # Do some basic checks on filter.
                    if not re.match(r'^\(.*\)$', search_params[k]):
                        # Outer parentheses not found thus invalid filter
                        raise ValueError("LDAP search filter invalid format")
                    if not search_params[k].count(
                            '(') == search_params[k].count(')'):
                        # The count of '(' and ')' characters need to match in filter.
                        raise LDAPInvalidFilterError(
                            "Invalid filter because of unmatched parentheses.")

        def update_param_fields(search_params):
            """"Update %ldap_param% fields

          Escape some characters in search_params[search_filter].
          If search_params[search_filter] hash key has %ldap_param% set in it's value, update
          value replacing %param% with actual escaped value from param.

          """
            for k in search_params:
                if re.match('^search_filter$', k):
                    # Escape some characters in search_filter which might cause LDAP injection.
                    search_params[k] = escape_chars(search_params[k])

                    # Search for "%ldap_param% token in parameter.
                    if re.search("%ldap_param%", search_params[k]):

                        # Only allow "%ldap_param% in search_filter field.
                        if re.match('^search_filter$', k):

                            if "param" not in search_params:
                                raise Exception(
                                    "The parameter '{}' contains string token '%ldap_param%' but the input '{}' is blank."
                                    .format(k, "ldap_search_param"))

                            else:
                                # Insert escaped param value in filter, need to escape any backslashes X 2 for regex.
                                search_params[k] = re.sub(
                                    "%ldap_param%",
                                    search_params["param"].replace(
                                        '\\', '\\\\'), search_params[k])
                                LOG.debug(('Transformed parameter' + k +
                                           ' to ' + search_params[k]))

                        else:
                            raise Exception(
                                "The string %ldap_param% not allowed in parameter '{}' "
                                .format(k))

            return search_params

        try:
            yield StatusMessage("Starting ldap_utilities_search")

            # Instansiate helper (which gets appconfigs from file)
            helper = LDAPUtilitiesHelper(self.options)
            yield StatusMessage("Appconfig Settings OK")

            # Get function inputs
            input_ldap_search_base = helper.get_function_input(
                kwargs, "ldap_search_base")  # text (required)
            input_ldap_search_filter = self.get_textarea_param(
                kwargs.get("ldap_search_filter"))  # textarea (required)
            input_ldap_search_attributes = helper.get_function_input(
                kwargs, "ldap_search_attributes",
                optional=True)  # text (optional)
            input_ldap_search_param = helper.get_function_input(
                kwargs, "ldap_search_param", optional=True)  # text (optional)

            if input_ldap_search_attributes is None:
                input_ldap_search_attributes = ""
            else:
                attributes = input_ldap_search_attributes.split(',')
                input_ldap_search_attributes = [
                    str(attr) for attr in attributes
                ]

            yield StatusMessage("Function Inputs OK")

            search_params = {
                'search_base': input_ldap_search_base,
                'search_filter': input_ldap_search_filter,
                'search_attributes': input_ldap_search_attributes
            }

            if input_ldap_search_param:
                # Escape 'param' parameter.
                search_params.setdefault(
                    'param', escape_filter_chars(input_ldap_search_param))

            yield StatusMessage("Validating LDAP Parameters")
            validate_params(search_params)
            search_params = update_param_fields(search_params)

            # Instansiate LDAP Server and Connection
            c = helper.get_ldap_connection()

            try:
                # Bind to the connection
                c.bind()
            except Exception as e:
                raise ValueError(
                    "Cannot connect to LDAP Server. Ensure credentials are correct"
                )

            # Inform user
            msg = ""
            if helper.LDAP_IS_ACTIVE_DIRECTORY:
                msg = "Connected to {0}".format("Active Directory")
            else:
                msg = "Connected to {0}".format("LDAP Server")
            yield StatusMessage(msg)

            res = False
            entries = []
            success = False

            try:
                yield StatusMessage("Attempting to Search")
                res = c.search(search_params["search_base"],
                               search_params["search_filter"],
                               attributes=search_params["search_attributes"])

                if res and len(c.entries) > 0:

                    entries = json.loads(c.response_to_json())["entries"]
                    LOG.info("Result contains %s entries", len(entries))

                    # Each entry has 'dn' and dict of 'attributes'. Move attributes to the top level for easier processing.
                    for entry in entries:
                        LOG.debug(json.dumps(entry))
                        entry.update(entry.pop("attributes", None))

                    yield StatusMessage("{0} entries found".format(
                        len(entries)))
                    success = True

                else:
                    yield StatusMessage("No entries found")
                    success = False

            except LDAPSocketOpenError:
                success = False
                raise ValueError("Invalid Search Base", input_ldap_search_base)

            except Exception as e:
                success = False
                raise ValueError(
                    "Could not Search the LDAP Server. Ensure 'ldap_search_base' is valid",
                    e)

            finally:
                # Unbind connection
                c.unbind()

            results = {"success": success, "entries": entries}

            LOG.info("Completed")
            LOG.debug(json.dumps(results))

            # Produce a FunctionResult with the return value.
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _ldap_utilities_add_to_groups_function(self, event, *args, **kwargs):
        """Function: A function that allows adding multiple users to multiple groups"""
        log = logging.getLogger(__name__)
        try:
            yield StatusMessage("Starting ldap_utilities_add_to_groups")

            # Instansiate helper (which gets appconfigs from file)
            helper = LDAPUtilitiesHelper(self.options)
            yield StatusMessage("Appconfig Settings OK")

            # Get function inputs
            input_ldap_multiple_user_dn_asString = helper.get_function_input(
                kwargs, "ldap_multiple_user_dn"
            )  # text (required) [string repersentation of an array]
            input_ldap_multiple_group_dn_asString = helper.get_function_input(
                kwargs, "ldap_multiple_group_dn"
            )  # text (required) [string repersentation of an array]
            yield StatusMessage("Function Inputs OK")

            if not helper.LDAP_IS_ACTIVE_DIRECTORY:
                raise FunctionError(
                    "This function only supports an Active Directory connection. Make sure ldap_is_active_directory is set to True in the app.config file"
                )

            try:
                # Try converting input to an array
                input_ldap_multiple_user_dn = literal_eval(
                    input_ldap_multiple_user_dn_asString)
                input_ldap_multiple_group_dn = literal_eval(
                    input_ldap_multiple_group_dn_asString)

            except Exception:
                raise ValueError(
                    """input_ldap_multiple_user_dn and input_ldap_multiple_group_dn must be a string repersenation of an array e.g. "['dn=Accounts Group,dc=example,dc=com', 'dn=IT Group,dc=example,dc=com']" """
                )

            # Instansiate LDAP Server and Connection
            c = helper.get_ldap_connection()

            try:
                # Bind to the connection
                c.bind()
            except Exception as err:
                raise ValueError(
                    "Cannot connect to LDAP Server. Ensure credentials are correct\n Error: {0}"
                    .format(err))

            # Inform user
            msg = "Connected to {0}".format("Active Directory")
            yield StatusMessage(msg)

            res = False

            try:
                yield StatusMessage("Attempting to add user(s) to group(s)")
                # perform the removeMermbersFromGroups operation
                res = ad_add_members_to_groups(c, input_ldap_multiple_user_dn,
                                               input_ldap_multiple_group_dn,
                                               True)

            except Exception:
                raise ValueError("Ensure all user and group DNs exist")

            finally:
                # Unbind connection
                c.unbind()

            results = {
                "success": res,
                "users_dn": input_ldap_multiple_user_dn,
                "groups_dn": input_ldap_multiple_group_dn
            }

            log.info("Completed")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
Exemplo n.º 4
0
    def _ldap_utilities_toggle_access_function(self, event, *args, **kwargs):
        """Function: A function that allows an LDAP user, with the correct privileges to enable or disable another user given their DN"""
        log = logging.getLogger(__name__)
        
        try:
            yield StatusMessage("Starting ldap_utilities_toggle_access")

            # Instansiate helper (which gets appconfigs from file)
            helper = LDAPUtilitiesHelper(self.options)
            yield StatusMessage("Appconfig Settings OK")

            # Get function inputs
            input_ldap_dn = helper.get_function_input(kwargs, "ldap_dn") # text (required)
            input_ldap_toggle_access = helper.get_function_input(kwargs, "ldap_toggle_access")["name"] # select, values: "Enable", "Disable" (required)
            yield StatusMessage("Function Inputs OK")

            if not helper.LDAP_IS_ACTIVE_DIRECTORY:
              raise FunctionError("This function only supports an Active Directory connection. Make sure ldap_is_active_directory is set to True in the app.config file")

            # Set local vars
            ldap_user_account_control_attribute = "userAccountControl"

            if (input_ldap_toggle_access.lower() == 'enable'):
              ldap_user_accout_control_value = 512
              user_status = "Enabled"

            elif (input_ldap_toggle_access.lower() == 'disable'):
              ldap_user_accout_control_value = 514
              user_status = "Disabled"

            else:
              raise ValueError("ldap_toggle_access function input must be 'Enable' or 'Disable'")

            # Instansiate LDAP Server and Connection
            c = helper.get_ldap_connection()
            
            try:
              # Bind to the connection
              c.bind()
            except:
              raise ValueError("Cannot connect to LDAP Server. Ensure credentials are correct")

            # Inform user
            msg = ""
            if helper.LDAP_IS_ACTIVE_DIRECTORY:
              msg = "Connected to {0}".format("Active Directory")
            else:
              msg = "Connected to {0}".format("LDAP Server")
            yield StatusMessage(msg)

            res = False

            try:
              yield StatusMessage("Attempting to {0} {1}".format(input_ldap_toggle_access, input_ldap_dn))
              # perform the Modify operation
              res = c.modify(input_ldap_dn, {ldap_user_account_control_attribute: [(MODIFY_REPLACE, [ldap_user_accout_control_value])]})

            except Exception:
              raise ValueError("Could not toggle access for this user. Ensue ldap_dn is valid")

            finally:
              # Unbind connection
              c.unbind()

            results = {
                "success": res,
                "user_dn": input_ldap_dn,
                "user_status": user_status
            }

            log.info("Completed")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _ldap_utilities_update_function(self, event, *args, **kwargs):
        """Function: A function that updates the attribute of a DN with a new value"""
        log = logging.getLogger(__name__)

        try:
            yield StatusMessage("Starting ldap_utilities_update")

            # Instansiate helper (which gets appconfigs from file)
            helper = LDAPUtilitiesHelper(self.options)
            yield StatusMessage("Appconfig Settings OK")

            # Get function inputs
            input_ldap_dn = helper.get_function_input(
                kwargs, "ldap_dn")  # text (required)
            input_ldap_attribute_name = helper.get_function_input(
                kwargs, "ldap_attribute_name")  # text (required)
            input_ldap_attribute_values_asString = helper.get_function_input(
                kwargs, "ldap_attribute_values"
            )  # text (required) [string repersentation of an array]
            yield StatusMessage("Function Inputs OK")

            try:
                # Try converting input to an array
                input_ldap_attribute_values = literal_eval(
                    input_ldap_attribute_values_asString)
            except Exception:
                raise ValueError(
                    """input_ldap_attribute_values must be a string repersenation of an array e.g. "['stringValue1, 1234, 'stringValue2']" """
                )

            # Instansiate LDAP Server and Connection
            c = helper.get_ldap_connection()

            try:
                # Bind to the connection
                c.bind()
            except:
                raise ValueError(
                    "Cannot connect to LDAP Server. Ensure credentials are correct"
                )

            # Inform user
            msg = ""
            if helper.LDAP_IS_ACTIVE_DIRECTORY:
                msg = "Connected to {0}".format("Active Directory")
            else:
                msg = "Connected to {0}".format("LDAP Server")
            yield StatusMessage(msg)

            res = False

            try:
                yield StatusMessage("Attempting to update {0}".format(
                    input_ldap_attribute_name))
                # perform the Modify operation
                res = c.modify(
                    input_ldap_dn, {
                        input_ldap_attribute_name:
                        [(MODIFY_REPLACE, input_ldap_attribute_values)]
                    })

            except Exception:
                raise ValueError(
                    "Failed to update. Ensure 'ldap_dn' is valid and the update meets your LDAP CONSTRAINTS"
                )

            finally:
                # Unbind connection
                c.unbind()

            results = {
                "success": res,
                "attribute_name": input_ldap_attribute_name,
                "attribute_values": input_ldap_attribute_values,
                "user_dn": input_ldap_dn
            }

            log.info("Completed")

            # Produce a FunctionResult with the results
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()
    def _ldap_utilities_search_function(self, event, *args, **kwargs):
        """Resilient Function: entry point """

        log = logging.getLogger(__name__)

        def replace_ldap_param(ldap_param_value=None, ldap_search_filter=""):

            re_pattern = "%ldap_param%"

            # if "ldap_param" in ldap_search_filter:

            if re.search(re_pattern, ldap_search_filter) is not None:
                if ldap_param_value is None:
                    raise ValueError(
                        "The LDAP Search Filter '{0}' contains the key '%ldap_param%' but no value has been given for ldap_search_param."
                        .format(ldap_search_filter))

                else:
                    # Insert escaped param value in filter, need to escape any backslashes X 2 for regex.
                    ldap_search_filter = re.sub(
                        re_pattern, ldap_param_value.replace('\\', '\\\\'),
                        ldap_search_filter)

            return ldap_search_filter

        try:
            yield StatusMessage("Starting ldap_utilities_search")

            # Instansiate helper (which gets appconfigs from file)
            helper = LDAPUtilitiesHelper(self.options)
            yield StatusMessage("Appconfig Settings OK")

            # Get function inputs
            input_ldap_search_base = helper.get_function_input(
                kwargs, "ldap_search_base")  # text (required)
            input_ldap_search_filter = self.get_textarea_param(
                kwargs.get("ldap_search_filter"))  # textarea (required)
            input_ldap_search_attributes = helper.get_function_input(
                kwargs, "ldap_search_attributes",
                optional=True)  # text (optional)
            input_ldap_search_param = helper.get_function_input(
                kwargs, "ldap_search_param", optional=True)  # text (optional)

            # If search_attributes is not specified, request that ALL_ATTRIBUTES for the DN be returned
            if input_ldap_search_attributes is None:
                input_ldap_search_attributes = ALL_ATTRIBUTES
            else:
                input_ldap_search_attributes = [
                    str(attr)
                    for attr in input_ldap_search_attributes.split(',')
                ]

            if input_ldap_search_param is not None:
                # Escape special chars from the search_param
                input_ldap_search_param = escape_filter_chars(
                    input_ldap_search_param)

            yield StatusMessage("Function Inputs OK")

            input_ldap_search_filter = replace_ldap_param(
                input_ldap_search_param, input_ldap_search_filter)

            # Instansiate LDAP Server and Connection
            conn = helper.get_ldap_connection()

            try:
                # Bind to the connection
                conn.bind()
            except Exception as err:
                raise ValueError(
                    "Cannot connect to LDAP Server. Ensure credentials are correct\n Error: {0}"
                    .format(err))

            try:
                # Inform user
                yield StatusMessage("Connected to {0}".format(
                    "Active Directory" if helper.
                    LDAP_IS_ACTIVE_DIRECTORY else "LDAP Server"))

                res = None
                entries = []
                success = False

                yield StatusMessage("Attempting to Search")

                res = conn.search(search_base=input_ldap_search_base,
                                  search_filter=input_ldap_search_filter,
                                  attributes=input_ldap_search_attributes)

                if res and len(conn.entries) > 0:

                    entries = json.loads(conn.response_to_json())["entries"]
                    log.info("Result contains %s entries", len(entries))

                    # Each entry has 'dn' and dict of 'attributes'. Move attributes to the top level for easier processing.
                    for entry in entries:
                        entry.update(entry.pop("attributes", None))

                    yield StatusMessage("{0} entries found".format(
                        len(entries)))
                    success = True

                else:
                    yield StatusMessage("No entries found")
                    success = False

            except LDAPSocketOpenError:
                success = False
                raise ValueError("Invalid Search Base", input_ldap_search_base)

            except Exception as err:
                success = False
                raise ValueError(
                    "Could not Search the LDAP Server. Ensure 'ldap_search_base' is valid",
                    err)

            finally:
                # Unbind connection
                conn.unbind()

            results = {"success": success, "entries": entries}

            log.info("Completed")
            log.debug("RESULTS: %s", results)

            # Produce a FunctionResult with the return value.
            yield FunctionResult(results)
        except Exception:
            yield FunctionError()