Skip to content

✨ Source dynamodb: Allow role based access #37530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ data:
connectorSubtype: api
connectorType: source
definitionId: 50401137-8871-4c5a-abb7-1f5fda35545a
dockerImageTag: 0.2.3
dockerImageTag: 0.3.0
dockerRepository: airbyte/source-dynamodb
documentationUrl: https://docs.airbyte.com/integrations/sources/dynamodb
githubIssueLabel: source-dynamodb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ public record DynamodbConfig(
) {

public static DynamodbConfig createDynamodbConfig(JsonNode jsonNode) {
JsonNode credentials = jsonNode.get("credentials");
JsonNode accessKeyId = credentials.get("access_key_id");
JsonNode secretAccessKey = credentials.get("secret_access_key");

JsonNode endpoint = jsonNode.get("endpoint");
JsonNode region = jsonNode.get("region");
JsonNode attributeNames = jsonNode.get("reserved_attribute_names");
return new DynamodbConfig(
endpoint != null && !endpoint.asText().isBlank() ? URI.create(endpoint.asText()) : null,
region != null && !region.asText().isBlank() ? Region.of(region.asText()) : null,
jsonNode.get("access_key_id").asText(),
jsonNode.get("secret_access_key").asText(),
accessKeyId != null && !accessKeyId.asText().isBlank() ? accessKeyId.asText() : null,
secretAccessKey != null && !secretAccessKey.asText().isBlank() ? secretAccessKey.asText() : null,
attributeNames != null ? Arrays.asList(attributeNames.asText().split("\\s*,\\s*")) : List.of());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,38 @@
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

public class DynamodbUtils {

private static final Logger LOGGER = LoggerFactory.getLogger(DynamodbUtils.class);

private DynamodbUtils() {

}

public static DynamoDbClient createDynamoDbClient(final DynamodbConfig dynamodbConfig) {
final var dynamoDbClientBuilder = DynamoDbClient.builder();
AwsCredentialsProvider awsCredentialsProvider;
if (!StringUtils.isBlank(dynamodbConfig.accessKey()) && !StringUtils.isBlank(dynamodbConfig.secretKey())) {
LOGGER.info("Creating credentials using access key and secret key");
AwsCredentials awsCreds = AwsBasicCredentials.create(dynamodbConfig.accessKey(), dynamodbConfig.secretKey());
awsCredentialsProvider = StaticCredentialsProvider.create(awsCreds);
} else {
LOGGER.info("Using Role Based Access");
awsCredentialsProvider = DefaultCredentialsProvider.create();
}

// configure access credentials
dynamoDbClientBuilder.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(dynamodbConfig.accessKey(), dynamodbConfig.secretKey())));
dynamoDbClientBuilder.credentialsProvider(awsCredentialsProvider);

if (dynamodbConfig.region() != null) {
dynamoDbClientBuilder.region(dynamodbConfig.region());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,55 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Dynamodb Source Spec",
"type": "object",
"required": ["access_key_id", "secret_access_key"],
"additionalProperties": false,
"properties": {
"credentials": {
"order": 0,
"type": "object",
"title": "Credentials",
"description": "Credentials for the service",
"oneOf": [
{
"title": "Authenticate via Access Keys",
"type": "object",
"required": ["access_key_id", "secret_access_key"],
"properties": {
"auth_type": {
"type": "string",
"const": "User",
"order": 0
},
"access_key_id": {
"order": 1,
"title": "Dynamodb Key Id",
"type": "string",
"description": "The access key id to access Dynamodb. Airbyte requires read permissions to the database",
"airbyte_secret": true,
"examples": ["A012345678910EXAMPLE"]
},
"secret_access_key": {
"order": 2,
"title": "Dynamodb Access Key",
"type": "string",
"description": "The corresponding secret to the access key id.",
"airbyte_secret": true,
"examples": ["a012345678910ABCDEFGH/AbCdEfGhEXAMPLEKEY"]
}
}
},
{
"type": "object",
"title": "Role Based Authentication",
"properties": {
"auth_type": {
"type": "string",
"const": "Role",
"order": 0
}
}
}
]
},
"endpoint": {
"title": "Dynamodb Endpoint",
"type": "string",
Expand Down Expand Up @@ -56,20 +102,6 @@
"us-west-2"
]
},
"access_key_id": {
"title": "Dynamodb Key Id",
"type": "string",
"description": "The access key id to access Dynamodb. Airbyte requires read permissions to the database",
"airbyte_secret": true,
"examples": ["A012345678910EXAMPLE"]
},
"secret_access_key": {
"title": "Dynamodb Access Key",
"type": "string",
"description": "The corresponding secret to the access key id.",
"airbyte_secret": true,
"examples": ["a012345678910ABCDEFGH/AbCdEfGhEXAMPLEKEY"]
},
"reserved_attribute_names": {
"title": "Reserved attribute names",
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
class DynamodbConfigTest {

@Test
void testDynamodbConfig() {
void testUserBasedDynamodbConfig() {

var jsonConfig = Jsons.jsonNode(Map.of(
"endpoint", "http://localhost:8080",
"region", "us-east-1",
"access_key_id", "A012345678910EXAMPLE",
"secret_access_key", "a012345678910ABCDEFGH/AbCdEfGhLEKEY"));
"credentials", Map.of("auth_type", "User", "access_key_id", "A012345678910EXAMPLE",
"secret_access_key", "a012345678910ABCDEFGH/AbCdEfGhLEKEY")));

var dynamodbConfig = DynamodbConfig.createDynamodbConfig(jsonConfig);

Expand All @@ -33,4 +33,18 @@ void testDynamodbConfig() {

}

@Test
void testRoleBasedDynamodbConfig() {
var jsonConfig = Jsons.jsonNode(Map.of(
"endpoint", "http://localhost:8080",
"region", "us-east-1",
"credentials", Map.of("auth_type", "Role")));

var dynamodbConfig = DynamodbConfig.createDynamodbConfig(jsonConfig);

assertThat(dynamodbConfig)
.hasFieldOrPropertyWithValue("endpoint", URI.create("http://localhost:8080"))
.hasFieldOrPropertyWithValue("region", Region.of("us-east-1"));
}

}
29 changes: 18 additions & 11 deletions docs/integrations/sources/dynamodb.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,31 @@ property type present in the previously generated schema:

This guide describes in details how you can configure the connector to connect with Dynamodb.

## Role Based Access

Defining **_access_key_id_** and **_secret_access_key_** will use User based Access. Role based access can be achieved
by omitting both values from the configuration. The connector will then use DefaultCredentialsProvider which will use
the underlying role executing the container workload in AWS.

### Сonfiguration Parameters

- **_endpoint_**: aws endpoint of the dynamodb instance
- **_region_**: the region code of the dynamodb instance
- **_access_key_id_**: the access key for the IAM user with the required permissions
- **_secret_access_key_**: the secret key for the IAM user with the required permissions
- (Optional) **_access_key_id_**: the access key for the IAM user with the required permissions. Omit for role based access.
- (Optional) **_secret_access_key_**: the secret key for the IAM user with the required permissions. Omit for role based access.
- **_reserved_attribute_names_**: comma separated list of attribute names present in the replication
tables which contain reserved words or special characters.
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html

## Changelog

| Version | Date | Pull Request | Subject |
|:--------| :--------- | :-------------------------------------------------------- |:---------------------------------------------------------------------|
| 0.2.3 | 2024-02-13 | [35232](https://github.com/airbytehq/airbyte/pull/35232) | Adopt CDK 0.20.4 |
| 0.2.2 | 2024-01-24 | [34453](https://github.com/airbytehq/airbyte/pull/34453) | bump CDK version |
| 0.2.1 | 2024-01-03 | [#33924](https://github.com/airbytehq/airbyte/pull/33924) | Add new ap-southeast-3 AWS region |
| 0.2.0 | 18-12-2023 | https://github.com/airbytehq/airbyte/pull/33485 | Remove LEGACY state |
| 0.1.2 | 01-19-2023 | https://github.com/airbytehq/airbyte/pull/20172 | Fix reserved words in projection expression & make them configurable |
| 0.1.1 | 02-09-2023 | https://github.com/airbytehq/airbyte/pull/22682 | Fix build |
| 0.1.0 | 11-14-2022 | https://github.com/airbytehq/airbyte/pull/18750 | Initial version |
| Version | Date | Pull Request | Subject |
|:--------| :--------- | :-------------------------------------------------------- |:-----------------------------------------------------------------------|
| 0.3.0 | 2024-04-24 | [37530](https://github.com/airbytehq/airbyte/pull/37530) | Allow role based access |
| 0.2.3 | 2024-02-13 | [35232](https://github.com/airbytehq/airbyte/pull/35232) | Adopt CDK 0.20.4 |
| 0.2.2 | 2024-01-24 | [34453](https://github.com/airbytehq/airbyte/pull/34453) | bump CDK version |
| 0.2.1 | 2024-01-03 | [#33924](https://github.com/airbytehq/airbyte/pull/33924) | Add new ap-southeast-3 AWS region |
| 0.2.0 | 18-12-2023 | https://github.com/airbytehq/airbyte/pull/33485 | Remove LEGACY state |
| 0.1.2 | 01-19-2023 | https://github.com/airbytehq/airbyte/pull/20172 | Fix reserved words in projection expression & make them configurable |
| 0.1.1 | 02-09-2023 | https://github.com/airbytehq/airbyte/pull/22682 | Fix build |
| 0.1.0 | 11-14-2022 | https://github.com/airbytehq/airbyte/pull/18750 | Initial version |
Loading