From 8525aa8b2a87694c03f0ba50736762059de8ed96 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 10 Oct 2017 16:55:36 +0100 Subject: [PATCH] Add AsDomainNameStringValidator and BaseAsValidator --- pom.xml | 4 + .../mda/validate/BaseAsValidator.java | 150 ++++++++++++++++++ .../string/AsDomainNameStringValidator.java | 42 +++++ .../uk/org/iay/incommon/mda/beans.xml | 3 + .../AsDomainNameStringValidatorTest.java | 108 +++++++++++++ 5 files changed, 307 insertions(+) create mode 100644 src/main/java/uk/org/iay/incommon/mda/validate/BaseAsValidator.java create mode 100644 src/main/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidator.java create mode 100644 src/test/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidatorTest.java diff --git a/pom.xml b/pom.xml index b0c475c..cd0667f 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,10 @@ com.google.code.findbugs jsr305 + + com.google.guava + guava + net.shibboleth.metadata aggregator-pipeline diff --git a/src/main/java/uk/org/iay/incommon/mda/validate/BaseAsValidator.java b/src/main/java/uk/org/iay/incommon/mda/validate/BaseAsValidator.java new file mode 100644 index 0000000..ec43c4d --- /dev/null +++ b/src/main/java/uk/org/iay/incommon/mda/validate/BaseAsValidator.java @@ -0,0 +1,150 @@ +/* + * 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 uk.org.iay.incommon.mda.validate; + +import java.util.List; + +import javax.annotation.Nonnull; + +import net.shibboleth.metadata.Item; +import net.shibboleth.metadata.pipeline.StageProcessingException; +import net.shibboleth.metadata.validate.Validator; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; + +/** + * An abstract base class for {@link Validator} implementations which validate a value of + * one type "as" another type. + * + *

The implementation calls a template method in the implementation subclass to perform the + * conversion. If the conversion succeeds, a sequence of {@link Validator}s are applied to + * that new value.

+ * + *

If the value cannot be converted to the new type, the template method is expected to + * throw {@link IllegalArgumentException}. In this case, behaviour depends on the + * {@link #conversionRequired} property.

+ * + *

If {@link #conversionRequired} is true (the default) then an error + * status will be applied to the {@link Item}, and the validator will return + * {@link net.shibboleth.metadata.validate.Validator.Action#DONE}.

+ * + *

If {@link #conversionRequired} is false then the validator + * will simply return {@link net.shibboleth.metadata.validate.Validator.Action#CONTINUE} + * so that subsequent validators may still be applied. This allows several "as" validators + * to be applied in sequence, each taking a different approach.

+ * + * @param type of the original value + * @param type of the new value to which validators should be applied + */ +public abstract class BaseAsValidator extends BaseLocalValidator implements Validator { + + /** The validator sequence to apply. */ + @Nonnull + private ValidatorSequence validators = new ValidatorSequence<>(); + + /** Whether conversion to the new type must succeed. Default: true */ + private boolean conversionRequired = true; + + /** + * Set the list of validators to apply to each item. + * + * @param newValidators the list of validators to set + */ + public void setValidators(@Nonnull final List> newValidators) { + validators.setValidators(newValidators); + } + + /** + * Gets the list of validators being applied to each item. + * + * @return list of validators + */ + @Nonnull + public List> getValidators() { + return validators.getValidators(); + } + + /** + * Set whether conversion to the new type is required to succeed. + * + * @param required true if the conversion is required to succeed + */ + public void setConversionRequired(final boolean required) { + conversionRequired = required; + } + + /** + * Returns whether conversion to the new type is required to succeed. + * + * @return true if the conversion is required to succeed + */ + public boolean isConversionRequired() { + return conversionRequired; + } + + /** + * Apply each of the configured validators in turn to the provided object. + * + * @param value object to be validated + * @param item the {@link Item} context for the validation + * + * @return the result of applying the validators to the value + * + * @throws StageProcessingException if errors occur during processing + */ + protected Action applyValidators(@Nonnull final A value, @Nonnull final Item item) + throws StageProcessingException { + return validators.validate(value, item, getId()); + } + + /** + * Convert from the old value type to the new. + * + * @param from a value of the old type + * @return a value of the new type + * @throws IllegalArgumentException if a conversion can not be performed + */ + protected abstract A convert(@Nonnull final V from) throws IllegalArgumentException; + + @Override + public Action validate(@Nonnull final V t, @Nonnull final Item item, @Nonnull final String stageId) + throws StageProcessingException { + try { + final A v = convert(t); + return applyValidators(v, item); + } catch (final IllegalArgumentException e) { + if (isConversionRequired()) { + addErrorMessage(t, item, stageId); + return Action.DONE; + } else { + return Action.CONTINUE; + } + } + } + + @Override + protected void doDestroy() { + validators.destroy(); + validators = null; + super.doDestroy(); + } + + @Override + protected void doInitialize() throws ComponentInitializationException { + super.doInitialize(); + validators.setId(getId()); + validators.initialize(); + } + +} diff --git a/src/main/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidator.java b/src/main/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidator.java new file mode 100644 index 0000000..2049ce9 --- /dev/null +++ b/src/main/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidator.java @@ -0,0 +1,42 @@ +/* + * 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 uk.org.iay.incommon.mda.validate.string; + +import javax.annotation.Nonnull; + +import com.google.common.net.InternetDomainName; + +import net.shibboleth.metadata.validate.Validator; +import uk.org.iay.incommon.mda.validate.BaseAsValidator; + +/** + * A Validator that checks {@link String} values as domain names by converting the + * value to an {@link InternetDomainName} and applying a sequence of validators to that value. + * + * This validator fails (and returns {@link net.shibboleth.metadata.validate.Validator.Action#DONE}) if the + * value can not be converted to an {@link InternetDomainName}. + * + * Otherwise, the validator applies the sequence of validators to the {@link InternetDomainName} and returns + * the value of that sequence. + */ +public class AsDomainNameStringValidator extends BaseAsValidator + implements Validator { + + @Override + protected InternetDomainName convert(@Nonnull final String domain) throws IllegalArgumentException { + return InternetDomainName.from(domain); + } + +} diff --git a/src/main/resources/uk/org/iay/incommon/mda/beans.xml b/src/main/resources/uk/org/iay/incommon/mda/beans.xml index cc9b55d..101f273 100644 --- a/src/main/resources/uk/org/iay/incommon/mda/beans.xml +++ b/src/main/resources/uk/org/iay/incommon/mda/beans.xml @@ -44,6 +44,9 @@ + + diff --git a/src/test/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidatorTest.java b/src/test/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidatorTest.java new file mode 100644 index 0000000..c96a0bf --- /dev/null +++ b/src/test/java/uk/org/iay/incommon/mda/validate/string/AsDomainNameStringValidatorTest.java @@ -0,0 +1,108 @@ + +package uk.org.iay.incommon.mda.validate.string; + +import java.util.ArrayList; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.common.net.InternetDomainName; + +import net.shibboleth.metadata.ErrorStatus; +import net.shibboleth.metadata.Item; +import net.shibboleth.metadata.pipeline.StageProcessingException; +import net.shibboleth.metadata.validate.Validator; +import net.shibboleth.metadata.validate.Validator.Action; +import uk.org.iay.incommon.mda.validate.BaseLocalValidator; +import uk.org.ukfederation.mda.MockItem; + +public class AsDomainNameStringValidatorTest { + + private static class CountingValidator extends BaseLocalValidator implements Validator { + public int count; + private final Action action; + + @Override + public Action validate(InternetDomainName e, Item item, String stageId) throws StageProcessingException { + count++; + return action; + } + + /** Constructor. */ + public CountingValidator(final Action a) { + action = a; + } + } + + @Test + public void testOK() throws Exception { + final CountingValidator counter = new CountingValidator(Action.CONTINUE); + counter.setId("counter"); + counter.initialize(); + + final List> nestedSequence = new ArrayList<>(); + nestedSequence.add(counter); + + final Item item = new MockItem("content"); + + final AsDomainNameStringValidator val = new AsDomainNameStringValidator(); + val.setId("id"); + val.setValidators(nestedSequence); + val.initialize(); + + final Action res = val.validate("example.org", item, "stage"); + Assert.assertEquals(res, Action.CONTINUE); + Assert.assertEquals(item.getItemMetadata().get(ErrorStatus.class).size(), 0); + Assert.assertEquals(counter.count, 1); + } + + @Test + public void testNoConvertDefault() throws Exception { + final CountingValidator counter = new CountingValidator(Action.CONTINUE); + counter.setId("counter"); + counter.initialize(); + + final List> nestedSequence = new ArrayList<>(); + nestedSequence.add(counter); + + final Item item = new MockItem("content"); + + final AsDomainNameStringValidator val = new AsDomainNameStringValidator(); + val.setId("id"); + val.setValidators(nestedSequence); + val.setMessage("quick brown %s"); + val.initialize(); + + final Action res = val.validate("example**.org", item, "stage"); + Assert.assertEquals(res, Action.DONE); + Assert.assertEquals(item.getItemMetadata().get(ErrorStatus.class).size(), 1); + final ErrorStatus err = item.getItemMetadata().get(ErrorStatus.class).get(0); + Assert.assertTrue(err.getStatusMessage().contains("quick brown example**.org")); + Assert.assertEquals(counter.count, 0); + } + + @Test + public void testNoConvertFalse() throws Exception { + final CountingValidator counter = new CountingValidator(Action.CONTINUE); + counter.setId("counter"); + counter.initialize(); + + final List> nestedSequence = new ArrayList<>(); + nestedSequence.add(counter); + + final Item item = new MockItem("content"); + + final AsDomainNameStringValidator val = new AsDomainNameStringValidator(); + val.setId("id"); + val.setConversionRequired(false); + val.setValidators(nestedSequence); + val.initialize(); + + final Action res = val.validate("example**.org", item, "stage"); + Assert.assertEquals(res, Action.CONTINUE); + Assert.assertEquals(item.getItemMetadata().get(ErrorStatus.class).size(), 0); + Assert.assertEquals(counter.count, 0); + } + +}