示例#1
0
def publish_message(topic_arn, req_data, subscription_arn=None):
    message = req_data['Message'][0]
    sqs_client = aws_stack.connect_to_service('sqs')
    for subscriber in SNS_SUBSCRIPTIONS.get(topic_arn, []):
        if subscription_arn not in [None, subscriber['SubscriptionArn']]:
            continue
        filter_policy = json.loads(subscriber.get('FilterPolicy') or '{}')
        message_attributes = get_message_attributes(req_data)
        if not check_filter_policy(filter_policy, message_attributes):
            continue
        if subscriber['Protocol'] == 'sqs':
            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
            try:
                sqs_client.send_message(
                    QueueUrl=queue_url,
                    MessageBody=create_sns_message_body(subscriber, req_data),
                    MessageAttributes=create_sqs_message_attributes(
                        subscriber, message_attributes))
            except Exception as exc:
                return make_error(message=str(exc), code=400)
        elif subscriber['Protocol'] == 'lambda':
            lambda_api.process_sns_notification(subscriber['Endpoint'],
                                                topic_arn,
                                                subscriber['SubscriptionArn'],
                                                message,
                                                message_attributes,
                                                subject=req_data.get(
                                                    'Subject', [None])[0])
        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)
            except Exception as exc:
                return make_error(message=str(exc), code=400)
            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)
        else:
            LOGGER.warning('Unexpected protocol "%s" for SNS subscription' %
                           subscriber['Protocol'])
示例#2
0
def publish_message(topic_arn, req_data):
    message = req_data['Message'][0]
    sqs_client = aws_stack.connect_to_service('sqs')
    for subscriber in SNS_SUBSCRIPTIONS.get(topic_arn, []):
        filter_policy = json.loads(subscriber.get('FilterPolicy', '{}'))
        message_attributes = get_message_attributes(req_data)
        if not check_filter_policy(filter_policy, message_attributes):
            continue
        if subscriber['Protocol'] == 'sqs':
            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
            try:
                sqs_client.send_message(
                    QueueUrl=queue_url,
                    MessageBody=create_sns_message_body(subscriber, req_data),
                    MessageAttributes=create_sqs_message_attributes(subscriber, message_attributes)
                )
            except Exception as exc:
                return make_error(message=str(exc), code=400)
        elif subscriber['Protocol'] == 'lambda':
            lambda_api.process_sns_notification(
                subscriber['Endpoint'],
                topic_arn,
                subscriber['SubscriptionArn'],
                message,
                message_attributes,
                subject=req_data.get('Subject', [None])[0]
            )
        elif subscriber['Protocol'] in ['http', 'https']:
            try:
                message_body = create_sns_message_body(subscriber, req_data)
            except Exception as exc:
                return make_error(message=str(exc), code=400)
            requests.post(
                subscriber['Endpoint'],
                headers={
                    'Content-Type': 'text/plain',
                    'x-amz-sns-message-type': 'Notification'
                },
                data=message_body
            )
        else:
            LOGGER.warning('Unexpected protocol "%s" for SNS subscription' % subscriber['Protocol'])
示例#3
0
def publish_message(topic_arn, req_data, subscription_arn=None):
    message = req_data['Message'][0]
    sqs_client = aws_stack.connect_to_service('sqs')

    LOG.debug('Publishing message to TopicArn: %s | Message:  %s' % (topic_arn, message))

    subscriptions = SNS_SUBSCRIPTIONS.get(topic_arn, [])
    for subscriber in list(subscriptions):
        if subscription_arn not in [None, subscriber['SubscriptionArn']]:
            continue
        filter_policy = json.loads(subscriber.get('FilterPolicy') or '{}')
        message_attributes = get_message_attributes(req_data)
        if not check_filter_policy(filter_policy, message_attributes):
            continue

        if 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

                sqs_client.send_message(
                    QueueUrl=queue_url,
                    MessageBody=create_sns_message_body(subscriber, req_data),
                    MessageAttributes=create_sqs_message_attributes(subscriber, message_attributes)
                )
            except Exception as 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)

        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_attributes,
                    unsubscribe_url,
                    subject=req_data.get('Subject', [None])[0]
                )
                if isinstance(response, FlaskResponse):
                    response.raise_for_status()
            except Exception as exc:
                LOG.warning('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))

        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)
            except Exception:
                continue
            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:
                sns_error_to_dead_letter_queue(subscriber['SubscriptionArn'], req_data, str(exc))
        else:
            LOG.warning('Unexpected protocol "%s" for SNS subscription' % subscriber['Protocol'])
示例#4
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"])
示例#5
0
def message_to_subscribers(message_id,
                           message,
                           topic_arn,
                           req_data,
                           subscription_arn=None,
                           skip_checks=False):
    subscriptions = SNS_SUBSCRIPTIONS.get(topic_arn, [])
    for subscriber in list(subscriptions):
        if subscription_arn not in [None, subscriber['SubscriptionArn']]:
            continue

        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))
            continue

        if subscriber['Protocol'] == 'sms':
            event = {
                'topic_arn': topic_arn,
                'endpoint': subscriber['Endpoint'],
                'message_content': req_data['Message'][0]
            }
            SMS_MESSAGES.append(event)
            LOG.info('Delivering SMS message to %s: %s',
                     subscriber['Endpoint'], req_data['Message'][0])

        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

                sqs_client = aws_stack.connect_to_service('sqs')
                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))
            except Exception as exc:
                LOG.warning('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)

        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.warning(
                    '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))

        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:
                continue
            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))

        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))

        else:
            LOG.warning('Unexpected protocol "%s" for SNS subscription' %
                        subscriber['Protocol'])
示例#6
0
    def forward_request(self, method, path, data, headers):

        if method == 'POST' and path == '/':
            req_data = urlparse.parse_qs(to_str(data))
            req_action = req_data['Action'][0]
            topic_arn = req_data.get('TargetArn') or req_data.get('TopicArn')

            if topic_arn:
                topic_arn = topic_arn[0]
                do_create_topic(topic_arn)

            if req_action == 'SetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(
                        message='Unable to find subscription for given ARN',
                        code=400)
                attr_name = req_data['AttributeName'][0]
                attr_value = req_data['AttributeValue'][0]
                sub[attr_name] = attr_value
                return make_response(req_action)
            elif req_action == 'GetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(
                        message='Unable to find subscription for given ARN',
                        code=400)
                content = '<Attributes>'
                for key, value in sub.items():
                    content += '<entry><key>%s</key><value>%s</value></entry>\n' % (
                        key, value)
                content += '</Attributes>'
                return make_response(req_action, content=content)
            elif req_action == 'Subscribe':
                if 'Endpoint' not in req_data:
                    return make_error(
                        message='Endpoint not specified in subscription',
                        code=400)
            elif req_action == 'Unsubscribe':
                if 'SubscriptionArn' not in req_data:
                    return make_error(
                        message=
                        'SubscriptionArn not specified in unsubscribe request',
                        code=400)
                do_unsubscribe(req_data.get('SubscriptionArn')[0])

            elif req_action == 'Publish':
                message = req_data['Message'][0]
                sqs_client = aws_stack.connect_to_service('sqs')
                for subscriber in SNS_SUBSCRIPTIONS[topic_arn]:
                    if subscriber['Protocol'] == 'sqs':
                        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
                        sqs_client.send_message(
                            QueueUrl=queue_url,
                            MessageBody=create_sns_message_body(
                                subscriber, req_data))
                    elif subscriber['Protocol'] == 'lambda':
                        lambda_api.process_sns_notification(
                            subscriber['Endpoint'],
                            topic_arn,
                            message,
                            subject=req_data.get('Subject'))
                    elif subscriber['Protocol'] in ['http', 'https']:
                        requests.post(subscriber['Endpoint'],
                                      headers={
                                          'Content-Type': 'text/plain',
                                          'x-amz-sns-message-type':
                                          'Notification'
                                      },
                                      data=create_sns_message_body(
                                          subscriber, req_data))
                    else:
                        LOGGER.warning(
                            'Unexpected protocol "%s" for SNS subscription' %
                            subscriber['Protocol'])
                # return response here because we do not want the request to be forwarded to SNS
                return make_response(req_action)

        return True
示例#7
0
    def forward_request(self, method, path, data, headers):

        if method == 'POST' and path == '/':
            req_data = urlparse.parse_qs(to_str(data))
            req_action = req_data['Action'][0]
            topic_arn = req_data.get('TargetArn') or req_data.get('TopicArn')

            if topic_arn:
                topic_arn = topic_arn[0]
                do_create_topic(topic_arn)

            if req_action == 'SetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(message='Unable to find subscription for given ARN', code=400)
                attr_name = req_data['AttributeName'][0]
                attr_value = req_data['AttributeValue'][0]
                sub[attr_name] = attr_value
                return make_response(req_action)
            elif req_action == 'GetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(message='Unable to find subscription for given ARN', code=400)
                content = '<Attributes>'
                for key, value in sub.items():
                    content += '<entry><key>%s</key><value>%s</value></entry>\n' % (key, value)
                content += '</Attributes>'
                return make_response(req_action, content=content)
            elif req_action == 'Subscribe':
                if 'Endpoint' not in req_data:
                    return make_error(message='Endpoint not specified in subscription', code=400)
            elif req_action == 'Unsubscribe':
                if 'SubscriptionArn' not in req_data:
                    return make_error(message='SubscriptionArn not specified in unsubscribe request', code=400)
                do_unsubscribe(req_data.get('SubscriptionArn')[0])

            elif req_action == 'Publish':
                message = req_data['Message'][0]
                sqs_client = aws_stack.connect_to_service('sqs')
                for subscriber in SNS_SUBSCRIPTIONS[topic_arn]:
                    filter_policy = json.loads(subscriber.get('FilterPolicy', '{}'))
                    message_attributes = get_message_attributes(req_data)
                    if check_filter_policy(filter_policy, message_attributes):
                        if subscriber['Protocol'] == 'sqs':
                            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
                            try:
                                sqs_client.send_message(
                                    QueueUrl=queue_url,
                                    MessageBody=create_sns_message_body(subscriber, req_data)
                                )
                            except Exception as exc:
                                return make_error(message=str(exc), code=400)
                        elif subscriber['Protocol'] == 'lambda':
                            lambda_api.process_sns_notification(
                                subscriber['Endpoint'],
                                topic_arn, message, subject=req_data.get('Subject', [None])[0]
                            )
                        elif subscriber['Protocol'] in ['http', 'https']:
                            try:
                                message_body = create_sns_message_body(subscriber, req_data)
                            except Exception as exc:
                                return make_error(message=str(exc), code=400)
                            requests.post(
                                subscriber['Endpoint'],
                                headers={
                                    'Content-Type': 'text/plain',
                                    'x-amz-sns-message-type': 'Notification'
                                },
                                data=message_body
                            )
                        else:
                            LOGGER.warning('Unexpected protocol "%s" for SNS subscription' % subscriber['Protocol'])
                # return response here because we do not want the request to be forwarded to SNS
                return make_response(req_action)

        return True
示例#8
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'])
示例#9
0
def update_sns(method,
               path,
               data,
               headers,
               response=None,
               return_forward_info=False):
    if return_forward_info:
        if method == 'POST' and path == '/':
            req_data = urlparse.parse_qs(data)
            topic_arn = req_data.get('TargetArn') or req_data.get('TopicArn')
            if topic_arn:
                topic_arn = topic_arn[0]
                if topic_arn not in SNS_SUBSCRIPTIONS:
                    SNS_SUBSCRIPTIONS[topic_arn] = []
            if 'Subscribe' in req_data['Action']:
                subscription = {
                    # http://docs.aws.amazon.com/cli/latest/reference/sns/get-subscription-attributes.html
                    'TopicArn': topic_arn,
                    'Endpoint': req_data['Endpoint'][0],
                    'Protocol': req_data['Protocol'][0],
                    'RawMessageDelivery': 'false'
                }
                SNS_SUBSCRIPTIONS[topic_arn].append(subscription)
            elif 'Publish' in req_data['Action']:
                message = req_data['Message'][0]
                sqs_client = aws_stack.connect_to_service('sqs')
                for subscriber in SNS_SUBSCRIPTIONS[topic_arn]:
                    if subscriber['Protocol'] == 'sqs':
                        queue_name = subscriber['Endpoint'].split(':')[5]
                        queue_url = subscriber.get('sqs_queue_url')
                        if not queue_url:
                            queue_url = aws_stack.get_sqs_queue_url(queue_name)
                            subscriber['sqs_queue_url'] = queue_url
                        sqs_client.send_message(
                            QueueUrl=queue_url,
                            MessageBody=create_sns_message_body(
                                subscriber, req_data))
                    elif subscriber['Protocol'] == 'lambda':
                        lambda_api.process_sns_notification(
                            subscriber['Endpoint'],
                            topic_arn,
                            message,
                            subject=req_data.get('Subject'))
                    elif subscriber['Protocol'] == 'http':
                        requests.post(subscriber['Endpoint'],
                                      headers={
                                          'Content-Type': 'text/plain',
                                          'x-amz-sns-message-type':
                                          'Notification'
                                      },
                                      data=json.dumps({
                                          'Type': 'Notification',
                                          'Message': message,
                                      }))
                    else:
                        LOGGER.warning(
                            'Unexpected protocol "%s" for SNS subscription' %
                            subscriber['Protocol'])
                # return response here because we do not want the request to be forwarded to SNS
                response = Response()
                response._content = """<PublishResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
                    <PublishResult><MessageId>n/a</MessageId></PublishResult>
                    <ResponseMetadata><RequestId>n/a</RequestId></ResponseMetadata></PublishResponse>"""
                response.status_code = 200
                return response
        return True
示例#10
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"])
示例#11
0
def update_sns(method,
               path,
               data,
               headers,
               response=None,
               return_forward_info=False):
    if return_forward_info:
        if method == 'POST' and path == '/':
            req_data = urlparse.parse_qs(data)
            req_action = req_data['Action'][0]
            topic_arn = req_data.get('TargetArn') or req_data.get('TopicArn')
            if topic_arn:
                topic_arn = topic_arn[0]
                if topic_arn not in SNS_SUBSCRIPTIONS:
                    SNS_SUBSCRIPTIONS[topic_arn] = []

            if req_action == 'SetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(
                        message='Unable to find subscription for given ARN',
                        code=400)
                attr_name = req_data['AttributeName'][0]
                attr_value = req_data['AttributeValue'][0]
                sub[attr_name] = attr_value
                return make_response(req_action)
            elif req_action == 'GetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(
                        message='Unable to find subscription for given ARN',
                        code=400)
                content = '<Attributes>'
                for key, value in sub.items():
                    content += '<entry><key>%s</key><value>%s</value></entry>\n' % (
                        key, value)
                content += '</Attributes>'
                return make_response(req_action, content=content)
            elif req_action == 'Subscribe':
                if 'Endpoint' not in req_data:
                    return make_error(
                        message='Endpoint not specified in subscription',
                        code=400)
            elif req_action == 'Publish':
                message = req_data['Message'][0]
                sqs_client = aws_stack.connect_to_service('sqs')
                for subscriber in SNS_SUBSCRIPTIONS[topic_arn]:
                    if subscriber['Protocol'] == 'sqs':
                        queue_name = subscriber['Endpoint'].split(':')[5]
                        queue_url = subscriber.get('sqs_queue_url')
                        if not queue_url:
                            queue_url = aws_stack.get_sqs_queue_url(queue_name)
                            subscriber['sqs_queue_url'] = queue_url
                        sqs_client.send_message(
                            QueueUrl=queue_url,
                            MessageBody=create_sns_message_body(
                                subscriber, req_data))
                    elif subscriber['Protocol'] == 'lambda':
                        lambda_api.process_sns_notification(
                            subscriber['Endpoint'],
                            topic_arn,
                            message,
                            subject=req_data.get('Subject'))
                    elif subscriber['Protocol'] == 'http':
                        requests.post(subscriber['Endpoint'],
                                      headers={
                                          'Content-Type': 'text/plain',
                                          'x-amz-sns-message-type':
                                          'Notification'
                                      },
                                      data=json.dumps({
                                          'Type': 'Notification',
                                          'Message': message,
                                      }))
                    else:
                        LOGGER.warning(
                            'Unexpected protocol "%s" for SNS subscription' %
                            subscriber['Protocol'])
                # return response here because we do not want the request to be forwarded to SNS
                return make_response(req_action)

        return True

    else:
        # This branch is executed by the proxy after we've already received a
        # response from the backend, hence we can utilize the "reponse" variable here
        if method == 'POST' and path == '/':
            req_data = urlparse.parse_qs(data)
            req_action = req_data['Action'][0]
            if req_action == 'Subscribe' and response.status_code < 400:
                response_data = xmltodict.parse(response.content)
                topic_arn = (req_data.get('TargetArn')
                             or req_data.get('TopicArn'))[0]
                sub_arn = response_data['SubscribeResponse'][
                    'SubscribeResult']['SubscriptionArn']
                subscription = {
                    # http://docs.aws.amazon.com/cli/latest/reference/sns/get-subscription-attributes.html
                    'TopicArn': topic_arn,
                    'Endpoint': req_data['Endpoint'][0],
                    'Protocol': req_data['Protocol'][0],
                    'SubscriptionArn': sub_arn,
                    'RawMessageDelivery': 'false'
                }
                SNS_SUBSCRIPTIONS[topic_arn].append(subscription)