Skip to content

Commit

Permalink
source amazon-seller-partner: allow to use IAM user arn or IAM role a…
Browse files Browse the repository at this point in the history
…rn (#12523)
  • Loading branch information
pecalleja committed May 4, 2022
1 parent dda50e1 commit 4a13b60
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- name: Amazon Seller Partner
sourceDefinitionId: e55879a8-0ef8-4557-abcf-ab34c53ec460
dockerRepository: airbyte/source-amazon-seller-partner
dockerImageTag: 0.2.15
dockerImageTag: 0.2.16
sourceType: api
documentationUrl: https://docs.airbyte.io/integrations/sources/amazon-seller-partner
icon: amazonsellerpartner.svg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@
type: "string"
path_in_connector_config:
- "client_secret"
- dockerImage: "airbyte/source-amazon-seller-partner:0.2.15"
- dockerImage: "airbyte/source-amazon-seller-partner:0.2.16"
spec:
documentationUrl: "https://docs.airbyte.io/integrations/sources/amazon-seller-partner"
changelogUrl: "https://docs.airbyte.io/integrations/sources/amazon-seller-partner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ RUN pip install .
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.2.15
LABEL io.airbyte.version=0.2.16
LABEL io.airbyte.name=airbyte/source-amazon-seller-partner
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@


from .source import SourceAmazonSellerPartner
from .source import ConnectorConfig

__all__ = ["SourceAmazonSellerPartner"]
__all__ = ["SourceAmazonSellerPartner", "ConnectorConfig"]
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Config:
airbyte_secret=True,
)
role_arn: str = Field(
description="Specifies the Amazon Resource Name (ARN) of an IAM role that you want to use to perform operations requested using this profile. (Needs permission to 'Assume Role' STS).",
description="Specifies the Amazon Resource Name (ARN) of an IAM user or role that you want to use to perform operations requested using this profile. (Needs permission to 'Assume Role' STS).",
title="Role ARN",
airbyte_secret=True,
)
Expand All @@ -92,9 +92,8 @@ class SourceAmazonSellerPartner(AbstractSource):
def _get_stream_kwargs(self, config: ConnectorConfig) -> Mapping[str, Any]:
endpoint, marketplace_id, region = get_marketplaces(config.aws_environment)[config.region]

boto3_client = boto3.client("sts", aws_access_key_id=config.aws_access_key, aws_secret_access_key=config.aws_secret_key)
role = boto3_client.assume_role(RoleArn=config.role_arn, RoleSessionName="guid")
role_creds = role["Credentials"]
sts_credentials = self.get_sts_credentials(config)
role_creds = sts_credentials["Credentials"]
aws_signature = AWSSignature(
service="execute-api",
aws_access_key_id=role_creds.get("AccessKeyId"),
Expand All @@ -121,6 +120,24 @@ def _get_stream_kwargs(self, config: ConnectorConfig) -> Mapping[str, Any]:
}
return stream_kwargs

def get_sts_credentials(self, config: ConnectorConfig) -> dict:
"""
We can only use a IAM User arn entity or a IAM Role entity.
If we use an IAM user arn entity in the connector configuration we need to get the credentials directly from the boto3 sts client
If we use an IAM role arn entity we need to invoke the assume_role from the boto3 sts client to get the credentials related to that role
:param config:
"""
boto3_client = boto3.client("sts", aws_access_key_id=config.aws_access_key, aws_secret_access_key=config.aws_secret_key)
*_, arn_resource = config.role_arn.split(":")
if arn_resource.startswith("user"):
sts_credentials = boto3_client.get_session_token()
elif arn_resource.startswith("role"):
sts_credentials = boto3_client.assume_role(RoleArn=config.role_arn, RoleSessionName="guid")
else:
raise ValueError("Invalid ARN, your ARN is not for a user or a role")
return sts_credentials

def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, Any]:
"""
Check connection to Amazon SP API by requesting the list of reports as this endpoint should be available for any config.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"role_arn": {
"title": "Role ARN",
"description": "Specifies the Amazon Resource Name (ARN) of an IAM role that you want to use to perform operations requested using this profile. (Needs permission to 'Assume Role' STS).",
"description": "Specifies the Amazon Resource Name (ARN) of an IAM user or role that you want to use to perform operations requested using this profile. (Needs permission to 'Assume Role' STS).",
"airbyte_secret": true,
"type": "string"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import pytest
from source_amazon_seller_partner import SourceAmazonSellerPartner
from source_amazon_seller_partner import ConnectorConfig
from airbyte_cdk.models import ConnectorSpecification
from airbyte_cdk.sources.streams import Stream
from unittest.mock import MagicMock
from source_amazon_seller_partner.source import boto3


@pytest.fixture
def connector_source():
return SourceAmazonSellerPartner()


@pytest.fixture
def connector_config():
return ConnectorConfig(
replication_start_date="2017-01-25T00:00:00Z",
refresh_token="Atzr|IwEBIP-abc123",
lwa_app_id="amzn1.application-oa2-client.abc123",
lwa_client_secret="abc123",
aws_access_key="aws_access_key",
aws_secret_key="aws_secret_key",
role_arn="arn:aws:iam::123456789098:role/some-role",
aws_environment="SANDBOX",
region="US"
)


@pytest.fixture
def sts_credentials():
return {
"Credentials": {
"AccessKeyId": "foo",
"SecretAccessKey": "bar",
"SessionToken": "foobar",
}
}


@pytest.fixture
def mock_boto_client(mocker, sts_credentials):
boto_client = MagicMock()
mocker.patch.object(boto3, "client", return_value=boto_client)
boto_client.assume_role.return_value = sts_credentials
boto_client.get_session_token.return_value = sts_credentials
return boto_client


def test_spec(connector_source):
assert isinstance(connector_source.spec(), ConnectorSpecification)


def test_streams(connector_source, connector_config, mock_boto_client):
for stream in connector_source.streams(connector_config):
assert isinstance(stream, Stream)


@pytest.mark.parametrize(
"arn",
("arn:aws:iam::123456789098:user/some-user", "arn:aws:iam::123456789098:role/some-role")
)
def test_stream_with_good_iam_arn_value(mock_boto_client, connector_source, connector_config, arn):
connector_config.role_arn = arn
result = connector_source.get_sts_credentials(connector_config)
assert "Credentials" in result
if "user" in arn:
mock_boto_client.get_session_token.assert_called_once()
if "role" in arn:
mock_boto_client.assume_role.assert_called_once_with(RoleArn=arn, RoleSessionName="guid")


def test_stream_with_bad_iam_arn_value(connector_source, connector_config, mock_boto_client):
connector_config.role_arn = "bad-arn"
with pytest.raises(ValueError) as e:
connector_source.get_sts_credentials(connector_config)
assert "Invalid" in e.message
3 changes: 2 additions & 1 deletion docs/integrations/sources/amazon-seller-partner.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ Information about rate limits you may find [here](https://github.com/amzn/sellin
## CHANGELOG

| Version | Date | Pull Request | Subject |
| :------- | :--------- | :------------------------------------------------------- | :--------------------------------------------------------------------- |
|:---------|:-----------|:---------------------------------------------------------| :--------------------------------------------------------------------- |
| `0.2.16` | 2022-05-04 | [\#9789](https://github.com/airbytehq/airbyte/pull/12523)| allow to use IAM user arn or IAM role arn |
| `0.2.15` | 2022-01-25 | [\#9789](https://github.com/airbytehq/airbyte/pull/9789) | Add stream FbaReplacementsReports |
| `0.2.14` | 2022-01-19 | [\#9621](https://github.com/airbytehq/airbyte/pull/9621) | Add GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_GENERAL report |
| `0.2.13` | 2022-01-18 | [\#9581](https://github.com/airbytehq/airbyte/pull/9581) | Change createdSince parameter to dataStartTime |
Expand Down
2 changes: 1 addition & 1 deletion octavia-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ NAME DOCKER REPOSITORY D
Airtable airbyte/source-airtable 0.1.1 14c6e7ea-97ed-4f5e-a7b5-25e9a80b8212
AWS CloudTrail airbyte/source-aws-cloudtrail 0.1.4 6ff047c0-f5d5-4ce5-8c81-204a830fa7e1
Amazon Ads airbyte/source-amazon-ads 0.1.3 c6b0a29e-1da9-4512-9002-7bfd0cba2246
Amazon Seller Partner airbyte/source-amazon-seller-partner 0.2.15 e55879a8-0ef8-4557-abcf-ab34c53ec460
Amazon Seller Partner airbyte/source-amazon-seller-partner 0.2.16 e55879a8-0ef8-4557-abcf-ab34c53ec460
```

#### `octavia list connectors destinations`
Expand Down

0 comments on commit 4a13b60

Please sign in to comment.