Skip to content

Commit b492598

Browse files
Pedro Callejasuhomud
Pedro Calleja
authored andcommitted
source amazon-seller-partner: allow to use IAM user arn or IAM role arn (#12523)
1 parent 70f4c82 commit b492598

File tree

9 files changed

+107
-11
lines changed

9 files changed

+107
-11
lines changed

airbyte-config/init/src/main/resources/seed/source_definitions.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
- name: Amazon Seller Partner
2323
sourceDefinitionId: e55879a8-0ef8-4557-abcf-ab34c53ec460
2424
dockerRepository: airbyte/source-amazon-seller-partner
25-
dockerImageTag: 0.2.15
25+
dockerImageTag: 0.2.16
2626
sourceType: api
2727
documentationUrl: https://docs.airbyte.io/integrations/sources/amazon-seller-partner
2828
icon: amazonsellerpartner.svg

airbyte-config/init/src/main/resources/seed/source_specs.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@
209209
type: "string"
210210
path_in_connector_config:
211211
- "client_secret"
212-
- dockerImage: "airbyte/source-amazon-seller-partner:0.2.15"
212+
- dockerImage: "airbyte/source-amazon-seller-partner:0.2.16"
213213
spec:
214214
documentationUrl: "https://docs.airbyte.io/integrations/sources/amazon-seller-partner"
215215
changelogUrl: "https://docs.airbyte.io/integrations/sources/amazon-seller-partner"

airbyte-integrations/connectors/source-amazon-seller-partner/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ RUN pip install .
1212
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
1313
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]
1414

15-
LABEL io.airbyte.version=0.2.15
15+
LABEL io.airbyte.version=0.2.16
1616
LABEL io.airbyte.name=airbyte/source-amazon-seller-partner

airbyte-integrations/connectors/source-amazon-seller-partner/source_amazon_seller_partner/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222

2323

2424
from .source import SourceAmazonSellerPartner
25+
from .source import ConnectorConfig
2526

26-
__all__ = ["SourceAmazonSellerPartner"]
27+
__all__ = ["SourceAmazonSellerPartner", "ConnectorConfig"]

airbyte-integrations/connectors/source-amazon-seller-partner/source_amazon_seller_partner/source.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class Config:
8080
airbyte_secret=True,
8181
)
8282
role_arn: str = Field(
83-
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).",
83+
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).",
8484
title="Role ARN",
8585
airbyte_secret=True,
8686
)
@@ -92,9 +92,8 @@ class SourceAmazonSellerPartner(AbstractSource):
9292
def _get_stream_kwargs(self, config: ConnectorConfig) -> Mapping[str, Any]:
9393
endpoint, marketplace_id, region = get_marketplaces(config.aws_environment)[config.region]
9494

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

123+
def get_sts_credentials(self, config: ConnectorConfig) -> dict:
124+
"""
125+
We can only use a IAM User arn entity or a IAM Role entity.
126+
If we use an IAM user arn entity in the connector configuration we need to get the credentials directly from the boto3 sts client
127+
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
128+
129+
:param config:
130+
"""
131+
boto3_client = boto3.client("sts", aws_access_key_id=config.aws_access_key, aws_secret_access_key=config.aws_secret_key)
132+
*_, arn_resource = config.role_arn.split(":")
133+
if arn_resource.startswith("user"):
134+
sts_credentials = boto3_client.get_session_token()
135+
elif arn_resource.startswith("role"):
136+
sts_credentials = boto3_client.assume_role(RoleArn=config.role_arn, RoleSessionName="guid")
137+
else:
138+
raise ValueError("Invalid ARN, your ARN is not for a user or a role")
139+
return sts_credentials
140+
124141
def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, Any]:
125142
"""
126143
Check connection to Amazon SP API by requesting the list of reports as this endpoint should be available for any config.

airbyte-integrations/connectors/source-amazon-seller-partner/source_amazon_seller_partner/spec.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
},
6868
"role_arn": {
6969
"title": "Role ARN",
70-
"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).",
70+
"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).",
7171
"airbyte_secret": true,
7272
"type": "string"
7373
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import pytest
2+
from source_amazon_seller_partner import SourceAmazonSellerPartner
3+
from source_amazon_seller_partner import ConnectorConfig
4+
from airbyte_cdk.models import ConnectorSpecification
5+
from airbyte_cdk.sources.streams import Stream
6+
from unittest.mock import MagicMock
7+
from source_amazon_seller_partner.source import boto3
8+
9+
10+
@pytest.fixture
11+
def connector_source():
12+
return SourceAmazonSellerPartner()
13+
14+
15+
@pytest.fixture
16+
def connector_config():
17+
return ConnectorConfig(
18+
replication_start_date="2017-01-25T00:00:00Z",
19+
refresh_token="Atzr|IwEBIP-abc123",
20+
lwa_app_id="amzn1.application-oa2-client.abc123",
21+
lwa_client_secret="abc123",
22+
aws_access_key="aws_access_key",
23+
aws_secret_key="aws_secret_key",
24+
role_arn="arn:aws:iam::123456789098:role/some-role",
25+
aws_environment="SANDBOX",
26+
region="US"
27+
)
28+
29+
30+
@pytest.fixture
31+
def sts_credentials():
32+
return {
33+
"Credentials": {
34+
"AccessKeyId": "foo",
35+
"SecretAccessKey": "bar",
36+
"SessionToken": "foobar",
37+
}
38+
}
39+
40+
41+
@pytest.fixture
42+
def mock_boto_client(mocker, sts_credentials):
43+
boto_client = MagicMock()
44+
mocker.patch.object(boto3, "client", return_value=boto_client)
45+
boto_client.assume_role.return_value = sts_credentials
46+
boto_client.get_session_token.return_value = sts_credentials
47+
return boto_client
48+
49+
50+
def test_spec(connector_source):
51+
assert isinstance(connector_source.spec(), ConnectorSpecification)
52+
53+
54+
def test_streams(connector_source, connector_config, mock_boto_client):
55+
for stream in connector_source.streams(connector_config):
56+
assert isinstance(stream, Stream)
57+
58+
59+
@pytest.mark.parametrize(
60+
"arn",
61+
("arn:aws:iam::123456789098:user/some-user", "arn:aws:iam::123456789098:role/some-role")
62+
)
63+
def test_stream_with_good_iam_arn_value(mock_boto_client, connector_source, connector_config, arn):
64+
connector_config.role_arn = arn
65+
result = connector_source.get_sts_credentials(connector_config)
66+
assert "Credentials" in result
67+
if "user" in arn:
68+
mock_boto_client.get_session_token.assert_called_once()
69+
if "role" in arn:
70+
mock_boto_client.assume_role.assert_called_once_with(RoleArn=arn, RoleSessionName="guid")
71+
72+
73+
def test_stream_with_bad_iam_arn_value(connector_source, connector_config, mock_boto_client):
74+
connector_config.role_arn = "bad-arn"
75+
with pytest.raises(ValueError) as e:
76+
connector_source.get_sts_credentials(connector_config)
77+
assert "Invalid" in e.message

docs/integrations/sources/amazon-seller-partner.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ Information about rate limits you may find [here](https://github.com/amzn/sellin
6767
## CHANGELOG
6868

6969
| Version | Date | Pull Request | Subject |
70-
| :------- | :--------- | :------------------------------------------------------- | :--------------------------------------------------------------------- |
70+
|:---------|:-----------|:---------------------------------------------------------| :--------------------------------------------------------------------- |
71+
| `0.2.16` | 2022-05-04 | [\#9789](https://github.com/airbytehq/airbyte/pull/12523)| allow to use IAM user arn or IAM role arn |
7172
| `0.2.15` | 2022-01-25 | [\#9789](https://github.com/airbytehq/airbyte/pull/9789) | Add stream FbaReplacementsReports |
7273
| `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 |
7374
| `0.2.13` | 2022-01-18 | [\#9581](https://github.com/airbytehq/airbyte/pull/9581) | Change createdSince parameter to dataStartTime |

octavia-cli/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ NAME DOCKER REPOSITORY D
188188
Airtable airbyte/source-airtable 0.1.1 14c6e7ea-97ed-4f5e-a7b5-25e9a80b8212
189189
AWS CloudTrail airbyte/source-aws-cloudtrail 0.1.4 6ff047c0-f5d5-4ce5-8c81-204a830fa7e1
190190
Amazon Ads airbyte/source-amazon-ads 0.1.3 c6b0a29e-1da9-4512-9002-7bfd0cba2246
191-
Amazon Seller Partner airbyte/source-amazon-seller-partner 0.2.15 e55879a8-0ef8-4557-abcf-ab34c53ec460
191+
Amazon Seller Partner airbyte/source-amazon-seller-partner 0.2.16 e55879a8-0ef8-4557-abcf-ab34c53ec460
192192
```
193193

194194
#### `octavia list connectors destinations`

0 commit comments

Comments
 (0)