Example #1
0
def do_forward_request_network(port, method, path, data, headers, target_url=None):
    # TODO: enable per-service endpoints, to allow deploying in distributed settings
    target_url = target_url or "%s://%s:%s" % (get_service_protocol(), LOCALHOST, port)
    url = "%s%s" % (target_url, path)
    response = requests.request(
        method, url, data=data, headers=headers, verify=False, stream=True, allow_redirects=False
    )
    return response
Example #2
0
def auth_keys_from_connection(connection: Dict):
    headers = {}

    auth_type = connection.get("AuthorizationType").upper()
    auth_parameters = connection.get("AuthParameters")
    if auth_type == AUTH_BASIC:
        basic_auth_parameters = auth_parameters.get("BasicAuthParameters", {})
        username = basic_auth_parameters.get("Username", "")
        password = basic_auth_parameters.get("Password", "")
        headers.update(
            {"authorization": "Basic {}:{}".format(username, password)})

    if auth_type == AUTH_API_KEY:
        api_key_parameters = auth_parameters.get("ApiKeyAuthParameters", {})
        api_key_name = api_key_parameters.get("ApiKeyName", "")
        api_key_value = api_key_parameters.get("ApiKeyValue", "")
        headers.update({api_key_name: api_key_value})

    if auth_type == AUTH_OAUTH:
        oauth_parameters = auth_parameters.get("OAuthParameters", {})

        oauth_method = oauth_parameters.get("HttpMethod")
        oauth_endpoint = oauth_parameters.get("AuthorizationEndpoint", "")
        query_object = list_of_parameters_to_object(
            oauth_parameters.get("QueryStringParameters", []))
        oauth_endpoint = add_query_params_to_url(oauth_endpoint, query_object)

        client_parameters = oauth_parameters.get("ClientParameters", {})
        client_id = client_parameters.get("ClientID", "")
        client_secret = client_parameters.get("ClientSecret", "")

        oauth_body = list_of_parameters_to_object(
            oauth_parameters.get("BodyParameters", []))
        oauth_body.update({
            "client_id": client_id,
            "client_secret": client_secret
        })

        oauth_header = list_of_parameters_to_object(
            oauth_parameters.get("HeaderParameters", []))
        oauth_result = requests.request(
            method=oauth_method,
            url=oauth_endpoint,
            data=json.dumps(oauth_body),
            headers=oauth_header,
        )

        oauth_data = json.loads(oauth_result.text)

        token_type = oauth_data.get("token_type", "")
        access_token = oauth_data.get("access_token", "")
        auth_header = "{} {}".format(token_type, access_token)
        headers.update({"authorization": auth_header})

    return headers
def send_event_to_api_destination(target_arn, event):
    """Send an event to an EventBridge API destination
    See https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-api-destinations.html"""

    # ARN format: ...:api-destination/{name}/{uuid}
    region = target_arn.split(":")[3]
    api_destination_name = target_arn.split(":")[-1].split("/")[1]
    events_client = connect_to_service("events", region_name=region)
    destination = events_client.describe_api_destination(
        Name=api_destination_name)

    # get destination endpoint details
    method = destination.get("HttpMethod", "GET")
    endpoint = destination.get("InvocationEndpoint")
    state = destination.get("ApiDestinationState") or "ACTIVE"

    LOG.debug('Calling EventBridge API destination (state "%s"): %s %s' %
              (state, method, endpoint))
    headers = {
        # default headers AWS sends with every api destination call
        "User-Agent": "Amazon/EventBridge/ApiDestinations",
        "Content-Type": "application/json; charset=utf-8",
        "Range": "bytes=0-1048575",
        "Accept-Encoding": "gzip,deflate",
        "Connection": "close",
    }

    # add auth headers for target destination
    add_api_destination_authorization(destination, headers, event)

    # TODO: consider option to disable the actual network call to avoid unintended side effects
    # TODO: InvocationRateLimitPerSecond (needs some form of thread-safety, scoped to the api destination)
    result = requests.request(method=method,
                              url=endpoint,
                              data=json.dumps(event or {}),
                              headers=headers)
    if result.status_code >= 400:
        LOG.debug("Received code %s forwarding events: %s %s" %
                  (result.status_code, method, endpoint))
        if result.status_code == 429 or 500 <= result.status_code <= 600:
            pass  # TODO: retry logic (only retry on 429 and 5xx response status)
Example #4
0
def send_event_to_target(arn, event, target_attributes=None, asynchronous=True):
    region = arn.split(':')[3]

    if ':lambda:' in arn:
        from localstack.services.awslambda import lambda_api
        lambda_api.run_lambda(func_arn=arn, event=event, context={}, asynchronous=asynchronous)

    elif ':sns:' in arn:
        sns_client = connect_to_service('sns', region_name=region)
        sns_client.publish(TopicArn=arn, Message=json.dumps(event))

    elif ':sqs:' in arn:
        sqs_client = connect_to_service('sqs', region_name=region)
        queue_url = get_sqs_queue_url(arn)
        msg_group_id = dict_utils.get_safe(target_attributes, '$.SqsParameters.MessageGroupId')
        kwargs = {'MessageGroupId': msg_group_id} if msg_group_id else {}
        sqs_client.send_message(QueueUrl=queue_url, MessageBody=json.dumps(event), **kwargs)

    elif ':states:' in arn:
        stepfunctions_client = connect_to_service('stepfunctions', region_name=region)
        stepfunctions_client.start_execution(stateMachineArn=arn, input=json.dumps(event))

    elif ':firehose:' in arn:
        delivery_stream_name = firehose_name(arn)
        firehose_client = connect_to_service('firehose', region_name=region)
        firehose_client.put_record(
            DeliveryStreamName=delivery_stream_name,
            Record={'Data': to_bytes(json.dumps(event))})

    elif ':events:' in arn:
        events_client = connect_to_service('events', region_name=region)
        target_name = arn.split(':')[-1].split('/')[-1]
        if ':destination/' in arn:
            destination = events_client.describe_api_destination(Name=target_name)
            method = destination.get('HttpMethod', 'GET')
            endpoint = destination.get('InvocationEndpoint')
            state = destination.get('ApiDestinationState') or 'ACTIVE'
            LOG.debug('Calling EventBridge API destination (state "%s"): %s %s' % (state, method, endpoint))
            result = requests.request(method=method, url=endpoint, data=json.dumps(event or {}))
            if result.status_code >= 400:
                LOG.debug('Received code %s forwarding events: %s %s' % (result.status_code, method, endpoint))
        else:
            events_client.put_events(
                Entries=[{
                    'EventBusName': target_name,
                    'Source': event.get('source'),
                    'DetailType': event.get('detail-type'),
                    'Detail': event.get('detail')
                }]
            )

    elif ':kinesis:' in arn:
        partition_key_path = dict_utils.get_safe(
            target_attributes,
            '$.KinesisParameters.PartitionKeyPath',
            default_value='$.id'
        )

        stream_name = arn.split('/')[-1]
        partition_key = dict_utils.get_safe(event, partition_key_path, event['id'])
        kinesis_client = connect_to_service('kinesis', region_name=region)

        kinesis_client.put_record(
            StreamName=stream_name,
            Data=to_bytes(json.dumps(event)),
            PartitionKey=partition_key
        )

    else:
        LOG.warning('Unsupported Events rule target ARN: "%s"' % arn)
Example #5
0
def send_event_to_target(target_arn, event, target_attributes=None, asynchronous=True):
    region = target_arn.split(":")[3]

    if ":lambda:" in target_arn:
        from localstack.services.awslambda import lambda_api

        lambda_api.run_lambda(
            func_arn=target_arn, event=event, context={}, asynchronous=asynchronous
        )

    elif ":sns:" in target_arn:
        sns_client = connect_to_service("sns", region_name=region)
        sns_client.publish(TopicArn=target_arn, Message=json.dumps(event))

    elif ":sqs:" in target_arn:
        sqs_client = connect_to_service("sqs", region_name=region)
        queue_url = get_sqs_queue_url(target_arn)
        msg_group_id = dict_utils.get_safe(target_attributes, "$.SqsParameters.MessageGroupId")
        kwargs = {"MessageGroupId": msg_group_id} if msg_group_id else {}
        sqs_client.send_message(QueueUrl=queue_url, MessageBody=json.dumps(event), **kwargs)

    elif ":states:" in target_arn:
        stepfunctions_client = connect_to_service("stepfunctions", region_name=region)
        stepfunctions_client.start_execution(stateMachineArn=target_arn, input=json.dumps(event))

    elif ":firehose:" in target_arn:
        delivery_stream_name = firehose_name(target_arn)
        firehose_client = connect_to_service("firehose", region_name=region)
        firehose_client.put_record(
            DeliveryStreamName=delivery_stream_name,
            Record={"Data": to_bytes(json.dumps(event))},
        )

    elif ":events:" in target_arn:
        events_client = connect_to_service("events", region_name=region)
        if ":api-destination/" in target_arn:
            # API destination support
            # see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-api-destinations.html
            api_destination_name = target_arn.split(":")[-1].split("/")[
                1
            ]  # ...:api-destination/{name}/{uuid}
            destination = events_client.describe_api_destination(Name=api_destination_name)
            method = destination.get("HttpMethod", "GET")
            endpoint = destination.get("InvocationEndpoint")
            state = destination.get("ApiDestinationState") or "ACTIVE"
            LOG.debug(
                'Calling EventBridge API destination (state "%s"): %s %s'
                % (state, method, endpoint)
            )
            # TODO: support connection/auth (BASIC AUTH, API KEY, OAUTH)
            # connection_arn = destination.get("ConnectionArn")
            headers = {
                # default headers AWS sends with every api destination call
                "User-Agent": "Amazon/EventBridge/ApiDestinations",
                "Content-Type": "application/json; charset=utf-8",
                "Range": "bytes=0-1048575",
                "Accept-Encoding": "gzip,deflate",
                "Connection": "close",
            }
            # TODO: consider option to disable the actual network call to avoid unintended side effects
            # TODO: InvocationRateLimitPerSecond (needs some form of thread-safety, scoped to the api destination)
            result = requests.request(
                method=method, url=endpoint, data=json.dumps(event or {}), headers=headers
            )
            if result.status_code >= 400:
                LOG.debug(
                    "Received code %s forwarding events: %s %s"
                    % (result.status_code, method, endpoint)
                )
                if result.status_code == 429 or 500 <= result.status_code <= 600:
                    pass  # TODO: retry logic (only retry on 429 and 5xx response status)
        else:
            eventbus_name = target_arn.split(":")[-1].split("/")[-1]
            events_client.put_events(
                Entries=[
                    {
                        "EventBusName": eventbus_name,
                        "Source": event.get("source"),
                        "DetailType": event.get("detail-type"),
                        "Detail": event.get("detail"),
                    }
                ]
            )

    elif ":kinesis:" in target_arn:
        partition_key_path = dict_utils.get_safe(
            target_attributes,
            "$.KinesisParameters.PartitionKeyPath",
            default_value="$.id",
        )

        stream_name = target_arn.split("/")[-1]
        partition_key = dict_utils.get_safe(event, partition_key_path, event["id"])
        kinesis_client = connect_to_service("kinesis", region_name=region)

        kinesis_client.put_record(
            StreamName=stream_name,
            Data=to_bytes(json.dumps(event)),
            PartitionKey=partition_key,
        )

    else:
        LOG.warning('Unsupported Events rule target ARN: "%s"' % target_arn)