Ejemplo n.º 1
0
async def message_to_subscriber(message_id, message, topic_arn, req_data,
                                headers, subscription_arn, skip_checks,
                                sns_backend, subscriber, subscriptions):

    if subscription_arn not in [None, subscriber['SubscriptionArn']]:
        return

    filter_policy = json.loads(subscriber.get('FilterPolicy') or '{}')
    message_attributes = get_message_attributes(req_data)
    if not skip_checks and not check_filter_policy(filter_policy,
                                                   message_attributes):
        LOG.info('SNS filter policy %s does not match attributes %s' %
                 (filter_policy, message_attributes))
        return
    if subscriber['Protocol'] == 'sms':
        event = {
            'topic_arn': topic_arn,
            'endpoint': subscriber['Endpoint'],
            'message_content': req_data['Message'][0]
        }
        sns_backend.sms_messages.append(event)
        LOG.info('Delivering SMS message to %s: %s', subscriber['Endpoint'],
                 req_data['Message'][0])
        return

    elif subscriber['Protocol'] == 'sqs':
        queue_url = None

        try:
            endpoint = subscriber['Endpoint']

            if 'sqs_queue_url' in subscriber:
                queue_url = subscriber.get('sqs_queue_url')
            elif '://' in endpoint:
                queue_url = endpoint
            else:
                queue_name = endpoint.split(':')[5]
                queue_url = aws_stack.get_sqs_queue_url(queue_name)
                subscriber['sqs_queue_url'] = queue_url

            message_group_id = req_data.get(
                'MessageGroupId')[0] if req_data.get('MessageGroupId') else ''

            sqs_client = aws_stack.connect_to_service('sqs')

            # TODO remove this kwargs if we stop using ElasticMQ entirely
            kwargs = {
                'MessageGroupId': message_group_id
            } if SQS_BACKEND_IMPL == 'moto' else {}
            sqs_client.send_message(
                QueueUrl=queue_url,
                MessageBody=create_sns_message_body(subscriber, req_data,
                                                    message_id),
                MessageAttributes=create_sqs_message_attributes(
                    subscriber, message_attributes),
                MessageSystemAttributes=create_sqs_system_attributes(headers),
                **kwargs)
        except Exception as exc:
            LOG.info('Unable to forward SNS message to SQS: %s %s' %
                     (exc, traceback.format_exc()))
            sns_error_to_dead_letter_queue(subscriber['SubscriptionArn'],
                                           req_data, str(exc))
            if 'NonExistentQueue' in str(exc):
                LOG.info(
                    'Removing non-existent queue "%s" subscribed to topic "%s"'
                    % (queue_url, topic_arn))
                subscriptions.remove(subscriber)
        return

    elif subscriber['Protocol'] == 'lambda':
        try:
            external_url = external_service_url('sns')
            unsubscribe_url = '%s/?Action=Unsubscribe&SubscriptionArn=%s' % (
                external_url, subscriber['SubscriptionArn'])
            response = lambda_api.process_sns_notification(
                subscriber['Endpoint'],
                topic_arn,
                subscriber['SubscriptionArn'],
                message,
                message_id,
                message_attributes,
                unsubscribe_url,
                subject=req_data.get('Subject', [None])[0])
            if isinstance(response, Response):
                response.raise_for_status()
            elif isinstance(response, FlaskResponse):
                if response.status_code >= 400:
                    raise Exception('Error response (code %s): %s' %
                                    (response.status_code, response.data))
        except Exception as exc:
            LOG.info('Unable to run Lambda function on SNS message: %s %s' %
                     (exc, traceback.format_exc()))
            sns_error_to_dead_letter_queue(subscriber['SubscriptionArn'],
                                           req_data, str(exc))
        return

    elif subscriber['Protocol'] in ['http', 'https']:
        msg_type = (req_data.get('Type') or ['Notification'])[0]
        try:
            message_body = create_sns_message_body(subscriber, req_data,
                                                   message_id)
        except Exception:
            return
        try:
            response = requests.post(
                subscriber['Endpoint'],
                headers={
                    'Content-Type': 'text/plain',
                    # AWS headers according to
                    # https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-header
                    'x-amz-sns-message-type': msg_type,
                    'x-amz-sns-topic-arn': subscriber['TopicArn'],
                    'x-amz-sns-subscription-arn':
                    subscriber['SubscriptionArn'],
                    'User-Agent': 'Amazon Simple Notification Service Agent'
                },
                data=message_body,
                verify=False)
            response.raise_for_status()
        except Exception as exc:
            LOG.info(
                'Received error on sending SNS message, putting to DLQ (if configured): %s'
                % exc)
            sns_error_to_dead_letter_queue(subscriber['SubscriptionArn'],
                                           req_data, str(exc))
        return

    elif subscriber['Protocol'] == 'application':
        try:
            sns_client = aws_stack.connect_to_service('sns')
            sns_client.publish(TargetArn=subscriber['Endpoint'],
                               Message=message)
        except Exception as exc:
            LOG.warning(
                'Unable to forward SNS message to SNS platform app: %s %s' %
                (exc, traceback.format_exc()))
            sns_error_to_dead_letter_queue(subscriber['SubscriptionArn'],
                                           req_data, str(exc))
        return

    elif subscriber['Protocol'] == 'email':
        ses_client = aws_stack.connect_to_service('ses')
        if subscriber.get('Endpoint'):
            ses_client.verify_email_address(
                EmailAddress=subscriber.get('Endpoint'))
            ses_client.verify_email_address(
                EmailAddress='*****@*****.**')

            ses_client.send_email(
                Source='*****@*****.**',
                Message={
                    'Body': {
                        'Text': {
                            'Data': message
                        }
                    },
                    'Subject': {
                        'Data': 'SNS-Subscriber-Endpoint'
                    }
                },
                Destination={'ToAddresses': [subscriber.get('Endpoint')]})
    else:
        LOG.warning('Unexpected protocol "%s" for SNS subscription' %
                    subscriber['Protocol'])
Ejemplo n.º 2
0
async def message_to_subscriber(
    message_id,
    message,
    topic_arn,
    req_data,
    headers,
    subscription_arn,
    skip_checks,
    sns_backend,
    subscriber,
    subscriptions,
    message_attributes,
):

    if subscription_arn not in [None, subscriber["SubscriptionArn"]]:
        return

    filter_policy = json.loads(subscriber.get("FilterPolicy") or "{}")
    if not message_attributes:
        message_attributes = get_message_attributes(req_data)
    if not skip_checks and not check_filter_policy(filter_policy,
                                                   message_attributes):
        LOG.info("SNS filter policy %s does not match attributes %s",
                 filter_policy, message_attributes)
        return
    if subscriber["Protocol"] == "sms":
        event = {
            "topic_arn": topic_arn,
            "endpoint": subscriber["Endpoint"],
            "message_content": req_data["Message"][0],
        }
        sns_backend.sms_messages.append(event)
        LOG.info(
            "Delivering SMS message to %s: %s",
            subscriber["Endpoint"],
            req_data["Message"][0],
        )

        # MOCK DATA
        delivery = {
            "phoneCarrier": "Mock Carrier",
            "mnc": 270,
            "priceInUSD": 0.00645,
            "smsType": "Transactional",
            "mcc": 310,
            "providerResponse": "Message has been accepted by phone carrier",
            "dwellTimeMsUntilDeviceAck": 200,
        }
        store_delivery_log(subscriber, True, message, message_id, delivery)
        return

    elif subscriber["Protocol"] == "sqs":
        queue_url = None

        try:
            endpoint = subscriber["Endpoint"]

            if "sqs_queue_url" in subscriber:
                queue_url = subscriber.get("sqs_queue_url")
            elif "://" in endpoint:
                queue_url = endpoint
            else:
                queue_name = endpoint.split(":")[5]
                queue_url = aws_stack.get_sqs_queue_url(queue_name)
                subscriber["sqs_queue_url"] = queue_url

            message_group_id = (req_data.get("MessageGroupId")
                                if req_data.get("MessageGroupId") else "")

            if isinstance(message_group_id, list):
                message_group_id = message_group_id[0]

            message_deduplication_id = (
                req_data.get("MessageDeduplicationId")[0]
                if req_data.get("MessageDeduplicationId") else "")

            sqs_client = aws_stack.connect_to_service("sqs")

            kwargs = {}
            if message_group_id:
                kwargs["MessageGroupId"] = message_group_id
            if message_deduplication_id:
                kwargs["MessageDeduplicationId"] = message_deduplication_id

            sqs_client.send_message(
                QueueUrl=queue_url,
                MessageBody=create_sns_message_body(subscriber, req_data,
                                                    message_id),
                MessageAttributes=create_sqs_message_attributes(
                    subscriber, message_attributes),
                MessageSystemAttributes=create_sqs_system_attributes(headers),
                **kwargs,
            )
            store_delivery_log(subscriber, True, message, message_id)
        except Exception as exc:
            LOG.info("Unable to forward SNS message to SQS: %s %s", exc,
                     traceback.format_exc())
            store_delivery_log(subscriber, False, message, message_id)
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
            if "NonExistentQueue" in str(exc):
                LOG.info(
                    'Removing non-existent queue "%s" subscribed to topic "%s"',
                    queue_url,
                    topic_arn,
                )
                subscriptions.remove(subscriber)
        return

    elif subscriber["Protocol"] == "lambda":
        try:
            external_url = external_service_url("sns")
            unsubscribe_url = "%s/?Action=Unsubscribe&SubscriptionArn=%s" % (
                external_url,
                subscriber["SubscriptionArn"],
            )
            response = lambda_api.process_sns_notification(
                subscriber["Endpoint"],
                topic_arn,
                subscriber["SubscriptionArn"],
                message,
                message_id,
                message_attributes,
                unsubscribe_url,
                subject=req_data.get("Subject", [None])[0],
            )

            if response is not None:
                delivery = {
                    "statusCode": response.status_code,
                    "providerResponse": response.get_data(),
                }
                store_delivery_log(subscriber, True, message, message_id,
                                   delivery)

            if isinstance(response, Response):
                response.raise_for_status()
            elif isinstance(response, FlaskResponse):
                if response.status_code >= 400:
                    raise Exception("Error response (code %s): %s" %
                                    (response.status_code, response.data))
        except Exception as exc:
            LOG.info("Unable to run Lambda function on SNS message: %s %s",
                     exc, traceback.format_exc())
            store_delivery_log(subscriber, False, message, message_id)
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
        return

    elif subscriber["Protocol"] in ["http", "https"]:
        msg_type = (req_data.get("Type") or ["Notification"])[0]
        try:
            message_body = create_sns_message_body(subscriber, req_data,
                                                   message_id)
        except Exception:
            return
        try:
            response = requests.post(
                subscriber["Endpoint"],
                headers={
                    "Content-Type": "text/plain",
                    # AWS headers according to
                    # https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-header
                    "x-amz-sns-message-type": msg_type,
                    "x-amz-sns-topic-arn": subscriber["TopicArn"],
                    "x-amz-sns-subscription-arn":
                    subscriber["SubscriptionArn"],
                    "User-Agent": "Amazon Simple Notification Service Agent",
                },
                data=message_body,
                verify=False,
            )

            delivery = {
                "statusCode": response.status_code,
                "providerResponse": response.content.decode("utf-8"),
            }
            store_delivery_log(subscriber, True, message, message_id, delivery)

            response.raise_for_status()
        except Exception as exc:
            LOG.info(
                "Received error on sending SNS message, putting to DLQ (if configured): %s",
                exc)
            store_delivery_log(subscriber, False, message, message_id)
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
        return

    elif subscriber["Protocol"] == "application":
        try:
            sns_client = aws_stack.connect_to_service("sns")
            sns_client.publish(TargetArn=subscriber["Endpoint"],
                               Message=message)
            store_delivery_log(subscriber, True, message, message_id)
        except Exception as exc:
            LOG.warning(
                "Unable to forward SNS message to SNS platform app: %s %s",
                exc,
                traceback.format_exc(),
            )
            store_delivery_log(subscriber, False, message, message_id)
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
        return

    elif subscriber["Protocol"] in ["email", "email-json"]:
        ses_client = aws_stack.connect_to_service("ses")
        if subscriber.get("Endpoint"):
            ses_client.verify_email_address(
                EmailAddress=subscriber.get("Endpoint"))
            ses_client.verify_email_address(
                EmailAddress="*****@*****.**")

            ses_client.send_email(
                Source="*****@*****.**",
                Message={
                    "Body": {
                        "Text": {
                            "Data":
                            create_sns_message_body(subscriber=subscriber,
                                                    req_data=req_data,
                                                    message_id=message_id) if
                            subscriber["Protocol"] == "email-json" else message
                        }
                    },
                    "Subject": {
                        "Data": "SNS-Subscriber-Endpoint"
                    },
                },
                Destination={"ToAddresses": [subscriber.get("Endpoint")]},
            )
            store_delivery_log(subscriber, True, message, message_id)
    else:
        LOG.warning('Unexpected protocol "%s" for SNS subscription',
                    subscriber["Protocol"])
Ejemplo n.º 3
0
async def message_to_subscriber(
    message_id,
    message,
    topic_arn,
    req_data,
    headers,
    subscription_arn,
    skip_checks,
    sns_backend,
    subscriber,
    subscriptions,
):

    if subscription_arn not in [None, subscriber["SubscriptionArn"]]:
        return

    filter_policy = json.loads(subscriber.get("FilterPolicy") or "{}")
    message_attributes = get_message_attributes(req_data)
    if not skip_checks and not check_filter_policy(filter_policy,
                                                   message_attributes):
        LOG.info("SNS filter policy %s does not match attributes %s" %
                 (filter_policy, message_attributes))
        return
    if subscriber["Protocol"] == "sms":
        event = {
            "topic_arn": topic_arn,
            "endpoint": subscriber["Endpoint"],
            "message_content": req_data["Message"][0],
        }
        sns_backend.sms_messages.append(event)
        LOG.info(
            "Delivering SMS message to %s: %s",
            subscriber["Endpoint"],
            req_data["Message"][0],
        )
        return

    elif subscriber["Protocol"] == "sqs":
        queue_url = None

        try:
            endpoint = subscriber["Endpoint"]

            if "sqs_queue_url" in subscriber:
                queue_url = subscriber.get("sqs_queue_url")
            elif "://" in endpoint:
                queue_url = endpoint
            else:
                queue_name = endpoint.split(":")[5]
                queue_url = aws_stack.get_sqs_queue_url(queue_name)
                subscriber["sqs_queue_url"] = queue_url

            message_group_id = (req_data.get("MessageGroupId")[0]
                                if req_data.get("MessageGroupId") else "")

            sqs_client = aws_stack.connect_to_service("sqs")

            # TODO remove this kwargs if we stop using ElasticMQ entirely
            kwargs = {
                "MessageGroupId": message_group_id
            } if SQS_BACKEND_IMPL == "moto" else {}
            sqs_client.send_message(
                QueueUrl=queue_url,
                MessageBody=create_sns_message_body(subscriber, req_data,
                                                    message_id),
                MessageAttributes=create_sqs_message_attributes(
                    subscriber, message_attributes),
                MessageSystemAttributes=create_sqs_system_attributes(headers),
                **kwargs,
            )
        except Exception as exc:
            LOG.info("Unable to forward SNS message to SQS: %s %s" %
                     (exc, traceback.format_exc()))
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
            if "NonExistentQueue" in str(exc):
                LOG.info(
                    'Removing non-existent queue "%s" subscribed to topic "%s"'
                    % (queue_url, topic_arn))
                subscriptions.remove(subscriber)
        return

    elif subscriber["Protocol"] == "lambda":
        try:
            external_url = external_service_url("sns")
            unsubscribe_url = "%s/?Action=Unsubscribe&SubscriptionArn=%s" % (
                external_url,
                subscriber["SubscriptionArn"],
            )
            response = lambda_api.process_sns_notification(
                subscriber["Endpoint"],
                topic_arn,
                subscriber["SubscriptionArn"],
                message,
                message_id,
                message_attributes,
                unsubscribe_url,
                subject=req_data.get("Subject", [None])[0],
            )
            if isinstance(response, Response):
                response.raise_for_status()
            elif isinstance(response, FlaskResponse):
                if response.status_code >= 400:
                    raise Exception("Error response (code %s): %s" %
                                    (response.status_code, response.data))
        except Exception as exc:
            LOG.info("Unable to run Lambda function on SNS message: %s %s" %
                     (exc, traceback.format_exc()))
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
        return

    elif subscriber["Protocol"] in ["http", "https"]:
        msg_type = (req_data.get("Type") or ["Notification"])[0]
        try:
            message_body = create_sns_message_body(subscriber, req_data,
                                                   message_id)
        except Exception:
            return
        try:
            response = requests.post(
                subscriber["Endpoint"],
                headers={
                    "Content-Type": "text/plain",
                    # AWS headers according to
                    # https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-header
                    "x-amz-sns-message-type": msg_type,
                    "x-amz-sns-topic-arn": subscriber["TopicArn"],
                    "x-amz-sns-subscription-arn":
                    subscriber["SubscriptionArn"],
                    "User-Agent": "Amazon Simple Notification Service Agent",
                },
                data=message_body,
                verify=False,
            )
            response.raise_for_status()
        except Exception as exc:
            LOG.info(
                "Received error on sending SNS message, putting to DLQ (if configured): %s"
                % exc)
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
        return

    elif subscriber["Protocol"] == "application":
        try:
            sns_client = aws_stack.connect_to_service("sns")
            sns_client.publish(TargetArn=subscriber["Endpoint"],
                               Message=message)
        except Exception as exc:
            LOG.warning(
                "Unable to forward SNS message to SNS platform app: %s %s" %
                (exc, traceback.format_exc()))
            sns_error_to_dead_letter_queue(subscriber["SubscriptionArn"],
                                           req_data, str(exc))
        return

    elif subscriber["Protocol"] == "email":
        ses_client = aws_stack.connect_to_service("ses")
        if subscriber.get("Endpoint"):
            ses_client.verify_email_address(
                EmailAddress=subscriber.get("Endpoint"))
            ses_client.verify_email_address(
                EmailAddress="*****@*****.**")

            ses_client.send_email(
                Source="*****@*****.**",
                Message={
                    "Body": {
                        "Text": {
                            "Data": message
                        }
                    },
                    "Subject": {
                        "Data": "SNS-Subscriber-Endpoint"
                    },
                },
                Destination={"ToAddresses": [subscriber.get("Endpoint")]},
            )
    else:
        LOG.warning('Unexpected protocol "%s" for SNS subscription' %
                    subscriber["Protocol"])