Skip to content

Commit

Permalink
Validate the AWS account Id in the S3 source when configuring either …
Browse files Browse the repository at this point in the history
…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 <dlv@amazon.com>
  • Loading branch information
dlvenable committed Apr 11, 2024
1 parent 501b335 commit e05a052
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 1 deletion.
1 change: 1 addition & 0 deletions data-prepper-plugins/aws-plugin-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -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<AwsAccountId, String> {
@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;
}
}
Original file line number Diff line number Diff line change
@@ -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));
}

}
Original file line number Diff line number Diff line change
@@ -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<ConstraintViolation<ValidatedClass>> 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<ConstraintViolation<ValidatedClass>> violations = validator.validate(validatedClass);

assertThat(violations, notNullValue());
assertThat(violations.size(), equalTo(1));
ConstraintViolation<ValidatedClass> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,9 +74,10 @@ public class S3SourceConfig {
private boolean disableBucketOwnershipValidation = false;

@JsonProperty("bucket_owners")
private Map<String, String> bucketOwners;
private Map<String, @AwsAccountId String> bucketOwners;

@JsonProperty("default_bucket_owner")
@AwsAccountId
private String defaultBucketOwner;

@JsonProperty("metadata_root_key")
Expand Down

0 comments on commit e05a052

Please sign in to comment.