def process(self, context, data):
        """
        Default interface for microservices. Process the input data for
        the input context.
        """
        self.context = context

        # Find the entityID for the SP that initiated the flow.
        try:
            sp_entity_id = context.state.state_dict['SATOSA_BASE']['requester']
        except KeyError as err:
            satosa_logging(logger, logging.ERROR, "Unable to determine the entityID for the SP requester", context.state)
            return super().process(context, data)

        satosa_logging(logger, logging.DEBUG, "entityID for the SP requester is {}".format(sp_entity_id), context.state)

        # Get the configuration for the SP.
        if sp_entity_id in self.config.keys():
            config = self.config[sp_entity_id]
        else:
            config = self.config['default']

        satosa_logging(logger, logging.DEBUG, "Using config {}".format(self._filter_config(config)), context.state)

        # Ignore this SP entirely if so configured.
        if config['ignore']:
            satosa_logging(logger, logging.INFO, "Ignoring SP {}".format(sp_entity_id), None)
            return super().process(context, data)

        # The list of values for the LDAP search filters that will be tried in order to find the
        # LDAP directory record for the user.
        filter_values = []

        # Loop over the configured list of identifiers from the IdP to consider and find
        # asserted values to construct the ordered list of values for the LDAP search filters.
        for candidate in config['ordered_identifier_candidates']:
            value = self._construct_filter_value(candidate, data)

            # If we have constructed a non empty value then add it as the next filter value
            # to use when searching for the user record.
            if value:
                filter_values.append(value)
                satosa_logging(logger, logging.DEBUG, "Added search filter value {} to list of search filters".format(value), context.state)

        # Initialize an empty LDAP record. The first LDAP record found using the ordered
        # list of search filter values will be the record used.
        record = None

        try:
            connection = config['connection']

            for filter_val in filter_values:
                if record:
                    break

                search_filter = '({0}={1})'.format(config['ldap_identifier_attribute'], filter_val)
                satosa_logging(logger, logging.DEBUG, "Constructed search filter {}".format(search_filter), context.state)

                satosa_logging(logger, logging.DEBUG, "Querying LDAP server...", context.state)
                message_id = connection.search(config['search_base'], search_filter, attributes=config['search_return_attributes'].keys())
                responses = connection.get_response(message_id)[0]
                satosa_logging(logger, logging.DEBUG, "Done querying LDAP server", context.state)
                satosa_logging(logger, logging.DEBUG, "LDAP server returned {} records".format(len(responses)), context.state)

                # for now consider only the first record found (if any)
                if len(responses) > 0:
                    if len(responses) > 1:
                        satosa_logging(logger, logging.WARN, "LDAP server returned {} records using search filter value {}".format(len(responses), filter_val), context.state)
                    record = responses[0]
                    break
        except LDAPException as err:
            satosa_logging(logger, logging.ERROR, "Caught LDAP exception: {}".format(err), context.state)
        except LdapAttributeStoreError as err:
            satosa_logging(logger, logging.ERROR, "Caught LDAP Attribute Store exception: {}".format(err), context.state)
        except Exception as err:
            satosa_logging(logger, logging.ERROR, "Caught unhandled exception: {}".format(err), context.state)
        else:
            err = None
        finally:
            if err:
                return super().process(context, data)

        # Before using a found record, if any, to populate attributes
        # clear any attributes incoming to this microservice if so configured.
        if config['clear_input_attributes']:
            satosa_logging(logger, logging.DEBUG, "Clearing values for these input attributes: {}".format(data.attributes), context.state)
            data.attributes = {}

        # Use a found record, if any, to populate attributes and input for NameID
        if record:
            satosa_logging(logger, logging.DEBUG, "Using record with DN {}".format(record["dn"]), context.state)
            satosa_logging(logger, logging.DEBUG, "Record with DN {} has attributes {}".format(record["dn"], record["attributes"]), context.state)

            # Populate attributes as configured.
            self._populate_attributes(config, record, context, data)

            # Populate input for NameID if configured. SATOSA core does the hashing of input
            # to create a persistent NameID.
            self._populate_input_for_name_id(config, record, context, data)

        else:
            satosa_logging(logger, logging.WARN, "No record found in LDAP so no attributes will be added", context.state)
            on_ldap_search_result_empty = config['on_ldap_search_result_empty']
            if on_ldap_search_result_empty:
                # Redirect to the configured URL with
                # the entityIDs for the target SP and IdP used by the user
                # as query string parameters (URL encoded).
                encoded_sp_entity_id = urllib.parse.quote_plus(sp_entity_id)
                encoded_idp_entity_id = urllib.parse.quote_plus(data.auth_info.issuer)
                url = "{}?sp={}&idp={}".format(on_ldap_search_result_empty, encoded_sp_entity_id, encoded_idp_entity_id)
                satosa_logging(logger, logging.INFO, "Redirecting to {}".format(url), context.state)
                return Redirect(url)

        satosa_logging(logger, logging.DEBUG, "Returning data.attributes {}".format(str(data.attributes)), context.state)
        return super().process(context, data)
Default interface for microservices. Process the input data for
        the input context.