From e05a05230e371ecc2af48a42a851f2cdbf07f9df Mon Sep 17 00:00:00 2001 From: David Venable Date: Thu, 11 Apr 2024 14:14:13 -0500 Subject: [PATCH] Validate the AWS account Id in the S3 source when configuring either the default_bucket_owner or the bucket_owners map. Implemented this by adding a new bean validation annotation @AwsAccountId in the aws-plugin-api. Resolves #4398 (#4400) Signed-off-by: David Venable --- .../aws-plugin-api/build.gradle | 1 + .../aws/validator/AwsAccountId.java | 28 +++++++++ .../AwsAccountIdConstraintValidator.java | 31 +++++++++ .../AwsAccountIdConstraintValidatorTest.java | 41 ++++++++++++ .../aws/validator/AwsAccountIdTest.java | 63 +++++++++++++++++++ .../plugins/source/s3/S3SourceConfig.java | 4 +- 6 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountId.java create mode 100644 data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidator.java create mode 100644 data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidatorTest.java create mode 100644 data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdTest.java diff --git a/data-prepper-plugins/aws-plugin-api/build.gradle b/data-prepper-plugins/aws-plugin-api/build.gradle index d0743c0c3e..9383c8c9f7 100644 --- a/data-prepper-plugins/aws-plugin-api/build.gradle +++ b/data-prepper-plugins/aws-plugin-api/build.gradle @@ -3,6 +3,7 @@ dependencies { implementation 'software.amazon.awssdk:auth' implementation 'software.amazon.awssdk:apache-client' implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1' + testImplementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' } test { diff --git a/data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountId.java b/data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountId.java new file mode 100644 index 0000000000..2f9a4eb937 --- /dev/null +++ b/data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountId.java @@ -0,0 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.aws.validator; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Add this annotation to validate that a given field as a valid AWS account Id. + */ +@Constraint(validatedBy = {AwsAccountIdConstraintValidator.class}) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE_USE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AwsAccountId { + String message() default "The value provided for an AWS account Id must be a valid AWS account Id with 12 digits."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidator.java b/data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidator.java new file mode 100644 index 0000000000..dc7fcc6a7f --- /dev/null +++ b/data-prepper-plugins/aws-plugin-api/src/main/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidator.java @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.aws.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * Validates than a value is a valid AWS account Id. This is intended for internal use + * only, but must be public to work with bean validation. + */ +public class AwsAccountIdConstraintValidator implements ConstraintValidator { + @Override + public boolean isValid(final String string, final ConstraintValidatorContext constraintValidatorContext) { + if(string == null) + return true; + + if(string.length() != 12) + return false; + + for(int i = 0; i < string.length(); i++) { + if(!Character.isDigit(string.charAt(i))) + return false; + } + + return true; + } +} diff --git a/data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidatorTest.java b/data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidatorTest.java new file mode 100644 index 0000000000..63b46363a7 --- /dev/null +++ b/data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdConstraintValidatorTest.java @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.aws.validator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +class AwsAccountIdConstraintValidatorTest { + + private static AwsAccountIdConstraintValidator createObjectUnderTest() { + return new AwsAccountIdConstraintValidator(); + } + + @Test + void isValid_with_null_returns_true() { + assertThat(createObjectUnderTest().isValid(null, null), + equalTo(true)); + } + + @ParameterizedTest + @ValueSource(strings = {"123456789012", "000011112222", "000000000000"}) + void isValid_with_valid_accountId_returns_true(final String stringValue) { + assertThat(createObjectUnderTest().isValid(stringValue, null), + equalTo(true)); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "12345678901", "1234567890123", "12345678901b", "a23456789012", "-23456789012"}) + void isValid_with_invalid_accountId_returns_false(final String stringValue) { + assertThat(createObjectUnderTest().isValid(stringValue, null), + equalTo(false)); + } + +} \ No newline at end of file diff --git a/data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdTest.java b/data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdTest.java new file mode 100644 index 0000000000..8168a3737f --- /dev/null +++ b/data-prepper-plugins/aws-plugin-api/src/test/java/org/opensearch/dataprepper/aws/validator/AwsAccountIdTest.java @@ -0,0 +1,63 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.aws.validator; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class AwsAccountIdTest { + private Validator validator; + + @BeforeEach + void setUp() { + validator = Validation.byDefaultProvider() + .configure() + .messageInterpolator(new ParameterMessageInterpolator()) + .buildValidatorFactory() + .getValidator(); + } + + @Test + void validate_works_with_actual_Validator_with_valid_accountId() { + final ValidatedClass validatedClass = new ValidatedClass(); + validatedClass.accountId = "123456789012"; + + final Set> violations = validator.validate(validatedClass); + + assertThat(violations, notNullValue()); + assertThat(violations.size(), equalTo(0)); + } + + @Test + void validate_works_with_actual_Validator_with_invalid_accountId() { + final ValidatedClass validatedClass = new ValidatedClass(); + validatedClass.accountId = "1234567890ab"; + + final Set> violations = validator.validate(validatedClass); + + assertThat(violations, notNullValue()); + assertThat(violations.size(), equalTo(1)); + ConstraintViolation actualViolation = violations.iterator().next(); + assertThat(actualViolation.getMessage(), containsString("AWS account Id")); + assertThat(actualViolation.getMessage(), containsString("12 digits")); + } + + private static class ValidatedClass { + @AwsAccountId + private String accountId; + } +} diff --git a/data-prepper-plugins/s3-source/src/main/java/org/opensearch/dataprepper/plugins/source/s3/S3SourceConfig.java b/data-prepper-plugins/s3-source/src/main/java/org/opensearch/dataprepper/plugins/source/s3/S3SourceConfig.java index a20a223fe5..b8b6313510 100644 --- a/data-prepper-plugins/s3-source/src/main/java/org/opensearch/dataprepper/plugins/source/s3/S3SourceConfig.java +++ b/data-prepper-plugins/s3-source/src/main/java/org/opensearch/dataprepper/plugins/source/s3/S3SourceConfig.java @@ -11,6 +11,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Max; +import org.opensearch.dataprepper.aws.validator.AwsAccountId; import org.opensearch.dataprepper.model.configuration.PluginModel; import org.opensearch.dataprepper.plugins.codec.CompressionOption; import org.opensearch.dataprepper.plugins.source.s3.configuration.AwsAuthenticationOptions; @@ -73,9 +74,10 @@ public class S3SourceConfig { private boolean disableBucketOwnershipValidation = false; @JsonProperty("bucket_owners") - private Map bucketOwners; + private Map bucketOwners; @JsonProperty("default_bucket_owner") + @AwsAccountId private String defaultBucketOwner; @JsonProperty("metadata_root_key")