From bedb19ead6f823253e344c3ab5b4035d112f6aff Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 12 Mar 2019 14:51:03 +0000 Subject: [PATCH 1/2] Add validator and validatoins for serverConfig.serverAddress and serverConfig.bindingAddress --- .../quorum/tessera/config/ServerConfig.java | 9 +- .../constraints/ServerAddressValidator.java | 67 ++++++++++ .../constraints/ValidServerAddress.java | 46 +++++++ .../quorum/tessera/config/ValidationTest.java | 38 ++++-- .../ServerAddressValidatorTest.java | 118 ++++++++++++++++++ 5 files changed, 269 insertions(+), 9 deletions(-) create mode 100644 config/src/main/java/com/quorum/tessera/config/constraints/ServerAddressValidator.java create mode 100644 config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java create mode 100644 config/src/test/java/com/quorum/tessera/config/constraints/ServerAddressValidatorTest.java diff --git a/config/src/main/java/com/quorum/tessera/config/ServerConfig.java b/config/src/main/java/com/quorum/tessera/config/ServerConfig.java index feeb7a50c4..70da063c5c 100644 --- a/config/src/main/java/com/quorum/tessera/config/ServerConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/ServerConfig.java @@ -1,5 +1,6 @@ package com.quorum.tessera.config; +import com.quorum.tessera.config.constraints.ValidServerAddress; import com.quorum.tessera.config.constraints.ValidSsl; import javax.validation.Valid; @@ -14,7 +15,6 @@ @XmlAccessorType(XmlAccessType.FIELD) public class ServerConfig extends ConfigItem { - //TODO validate that the server socket type and the communication type match the AppType @NotNull @XmlElement(required = true) private AppType app; @@ -23,7 +23,6 @@ public class ServerConfig extends ConfigItem { @XmlElement(required = true) private boolean enabled; - @XmlElement private CommunicationType communicationType; @@ -36,9 +35,15 @@ public class ServerConfig extends ConfigItem { @XmlElement private InfluxConfig influxConfig; + @ValidServerAddress( + message = "Binding Address is invalid", + isBindingAddress = true, + supportedSchemes = {"http","https"} + ) @XmlElement private String bindingAddress; + @ValidServerAddress(message = "Server Address is invalid") @NotNull @XmlElement private String serverAddress; diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/ServerAddressValidator.java b/config/src/main/java/com/quorum/tessera/config/constraints/ServerAddressValidator.java new file mode 100644 index 0000000000..5a0b6f4323 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/constraints/ServerAddressValidator.java @@ -0,0 +1,67 @@ +package com.quorum.tessera.config.constraints; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class ServerAddressValidator implements ConstraintValidator { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServerAddressValidator.class); + + private boolean bindingAddress; + + private List supportedSchemes; + + @Override + public void initialize(ValidServerAddress a) { + this.supportedSchemes = Arrays.asList(a.supportedSchemes()); + this.bindingAddress = a.isBindingAddress(); + } + + @Override + public boolean isValid(String v, ConstraintValidatorContext cvc) { + + if(Objects.isNull(v)) { + return true; + } + + final URI uri; + try{ + uri = new URI(v); + } catch (URISyntaxException ex) { + LOGGER.debug(v,ex); + return false; + } + + String scheme = uri.getScheme(); + + if(!supportedSchemes.contains(scheme)) { + return false; + } + + if(scheme.startsWith("http")) { + if(uri.getPort() == -1) { + return false; + } + } + + if(bindingAddress) { + return true; + } + + if(Objects.equals("unix",scheme)) { + return true; + } + + return !Objects.equals("0.0.0.0", uri.getHost()); + + } + +} diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java b/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java new file mode 100644 index 0000000000..14e7ce5867 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019 mark. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.quorum.tessera.config.constraints; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = ServerAddressValidator.class) +@Documented +public @interface ValidServerAddress { + + boolean isBindingAddress() default false; + + String[] supportedSchemes() default {"unix","http","https"}; + + String message() default "{ValidServerAddress.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java index fa40efd1a8..99dd69745e 100644 --- a/config/src/test/java/com/quorum/tessera/config/ValidationTest.java +++ b/config/src/test/java/com/quorum/tessera/config/ValidationTest.java @@ -127,7 +127,7 @@ public void invalidAlwaysSendTo() { List alwaysSendTo = singletonList("BOGUS"); - Config config = new Config(null, null, null, null, alwaysSendTo, null, false,false); + Config config = new Config(null, null, null, null, alwaysSendTo, null, false, false); Set> violations = validator.validateProperty(config, "alwaysSendTo"); @@ -145,7 +145,7 @@ public void validAlwaysSendTo() { List alwaysSendTo = singletonList(value); - Config config = new Config(null, null, null, null, alwaysSendTo, null, false,false); + Config config = new Config(null, null, null, null, alwaysSendTo, null, false, false); Set> violations = validator.validateProperty(config, "alwaysSendTo"); @@ -174,7 +174,7 @@ public void keypairPathsValidation() { assertThat(violation2.getMessageTemplate()).isEqualTo("File does not exist"); final List paths = Arrays.asList( - violation1.getPropertyPath().toString(), violation2.getPropertyPath().toString() + violation1.getPropertyPath().toString(), violation2.getPropertyPath().toString() ); assertThat(paths).containsExactlyInAnyOrder("keyData[0].publicKeyPath", "keyData[0].privateKeyPath"); } @@ -213,8 +213,8 @@ public void azureKeyPairIdsDisallowedCharactersCreateViolation() { assertThat(violations).hasSize(2); assertThat(violations).extracting("messageTemplate") - .containsExactly("Azure Key Vault key IDs can only contain alphanumeric characters and dashes (-)", - "Azure Key Vault key IDs can only contain alphanumeric characters and dashes (-)"); + .containsExactly("Azure Key Vault key IDs can only contain alphanumeric characters and dashes (-)", + "Azure Key Vault key IDs can only contain alphanumeric characters and dashes (-)"); } @Test @@ -235,7 +235,7 @@ public void azureKeyPairKeyVersionLongerThan32CharsCreatesViolation() { assertThat(violations).hasSize(2); assertThat(violations).extracting("messageTemplate") - .containsExactly("length must be 32 characters", "length must be 32 characters"); + .containsExactly("length must be 32 characters", "length must be 32 characters"); } @Test @@ -247,7 +247,7 @@ public void azureKeyPairKeyVersionShorterThan32CharsCreatesViolation() { assertThat(violations).hasSize(2); assertThat(violations).extracting("messageTemplate") - .containsExactly("length must be 32 characters", "length must be 32 characters"); + .containsExactly("length must be 32 characters", "length must be 32 characters"); } @Test @@ -418,4 +418,28 @@ public void hashicorpVaultKeyPairProvidedButKeyVaultConfigHasNullUrlCreatesNotNu assertThat(violation.getMessageTemplate()).isEqualTo("{javax.validation.constraints.NotNull.message}"); assertThat(violation.getPropertyPath().toString()).isEqualTo("hashicorpKeyVaultConfig.url"); } + + @Test + public void serverAddressValidations() { + + String[] invalidAddresses = {"/foo/bar","foo@bar.com,:/fff.ll","file:/tmp/valid.somename"}; + + ServerConfig config = new ServerConfig(); + for (String sample : invalidAddresses) { + config.setServerAddress(sample); + Set> validresult = validator.validateProperty(config, "serverAddress"); + assertThat(validresult).hasSize(1); + } + + + + String[] validSamples = {"unix:/foo/bar.ipc","http://localhost:8080","https://somestrangedomain.com:8080"}; + for (String sample : validSamples) { + config.setServerAddress(sample); + Set> validresult = validator.validateProperty(config, "serverAddress"); + assertThat(validresult).isEmpty(); + } + + } + } diff --git a/config/src/test/java/com/quorum/tessera/config/constraints/ServerAddressValidatorTest.java b/config/src/test/java/com/quorum/tessera/config/constraints/ServerAddressValidatorTest.java new file mode 100644 index 0000000000..02668e59a7 --- /dev/null +++ b/config/src/test/java/com/quorum/tessera/config/constraints/ServerAddressValidatorTest.java @@ -0,0 +1,118 @@ +package com.quorum.tessera.config.constraints; + +import javax.validation.ConstraintValidatorContext; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ServerAddressValidatorTest { + + @Test + public void valid() { + + ServerAddressValidator validator + = new ServerAddressValidator(); + + ValidServerAddress validServerAddress = mock(ValidServerAddress.class); + when(validServerAddress.supportedSchemes()).thenReturn(new String[]{"http"}); + + validator.initialize(validServerAddress); + + ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + + assertThat(validator.isValid("https://www.ilovesparraws.com:80/somepatth", context)).isFalse(); + assertThat(validator.isValid("http://www.ilovesparraws.com:80/somepatth", context)).isTrue(); + } + + @Test + public void dontAllowZeroIpWhenNotBindingAddress() { + + ServerAddressValidator validator + = new ServerAddressValidator(); + + ValidServerAddress validServerAddress = mock(ValidServerAddress.class); + + when(validServerAddress.isBindingAddress()).thenReturn(false); + when(validServerAddress.supportedSchemes()).thenReturn(new String[]{"http"}); + + validator.initialize(validServerAddress); + + ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + + assertThat(validator.isValid("http://0.0.0.0:80", context)).isFalse(); + + } + + @Test + public void dontAllowZeroIpWhenBindingAddress() { + + ServerAddressValidator validator + = new ServerAddressValidator(); + + ValidServerAddress validServerAddress = mock(ValidServerAddress.class); + + when(validServerAddress.isBindingAddress()).thenReturn(true); + when(validServerAddress.supportedSchemes()).thenReturn(new String[]{"http"}); + + validator.initialize(validServerAddress); + + ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + + assertThat(validator.isValid("http://0.0.0.0:80", context)).isTrue(); + + } + + @Test + public void nullValueIsIgnored() { + + ServerAddressValidator validator = new ServerAddressValidator(); + + ValidServerAddress validServerAddress = mock(ValidServerAddress.class); + + when(validServerAddress.isBindingAddress()).thenReturn(true); + when(validServerAddress.supportedSchemes()).thenReturn(new String[]{"http"}); + + validator.initialize(validServerAddress); + + ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + + assertThat(validator.isValid(null, context)).isTrue(); + + } + + @Test + public void unixSchemStypeIntCheckedForZeroIp() { + + ServerAddressValidator validator = new ServerAddressValidator(); + + ValidServerAddress validServerAddress = mock(ValidServerAddress.class); + + when(validServerAddress.isBindingAddress()).thenReturn(false); + when(validServerAddress.supportedSchemes()).thenReturn(new String[]{"unix"}); + + validator.initialize(validServerAddress); + + ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + + assertThat(validator.isValid("unix:/bogus", context)).isTrue(); + + } + + @Test + public void httpPortIsRequired() { + + ServerAddressValidator validator + = new ServerAddressValidator(); + + ValidServerAddress validServerAddress = mock(ValidServerAddress.class); + when(validServerAddress.supportedSchemes()).thenReturn(new String[]{"http"}); + + validator.initialize(validServerAddress); + + ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + + assertThat(validator.isValid("http://www.ilovesparraws.com", context)).isFalse(); + } + +} From 0250ef935681828348f1d737d448811d0fde5d97 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 12 Mar 2019 15:17:46 +0000 Subject: [PATCH 2/2] Remove ide generated license block --- .../config/constraints/ValidServerAddress.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java b/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java index 14e7ce5867..d58278ee14 100644 --- a/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java +++ b/config/src/main/java/com/quorum/tessera/config/constraints/ValidServerAddress.java @@ -1,18 +1,3 @@ -/* - * Copyright 2019 mark. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.quorum.tessera.config.constraints; import java.lang.annotation.Documented;