From fe3cbd50fb7a0b5959d3d5221154343744abb745 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Jul 2024 14:02:19 +0200 Subject: [PATCH] call named-checkzone in validateContext --- ...HsDomainDnsSetupHostingAssetValidator.java | 23 +++++++++- ...DnsSetupHostingAssetValidatorUnitTest.java | 42 +++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index f15404e5..bd6a1cb2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -1,10 +1,14 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; +import lombok.SneakyThrows; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; +import net.hostsharing.hsadminng.system.SystemProcess; +import java.util.List; import java.util.regex.Pattern; +import static java.util.Arrays.stream; import static net.hostsharing.hsadminng.hs.validation.ArrayProperty.arrayOf; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; @@ -17,8 +21,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato static final String RR_REGEX_TTL = "(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*"; static final String RR_REGEX_IN = "IN\\s+"; // record class IN for Internet static final String RR_RECORD_TYPE = "[A-Z]+\\s+"; - static final String RR_RECORD_DATA_X = "([a-z0-9\\.-]+|\"[^\"]*\")\\s*"; // FIXME: (...) and multiline? - static final String RR_RECORD_DATA = "([a-z0-9\\.-]+|\\([^\\)]*\\)|\"[^\"]*\")\\s*"; + static final String RR_RECORD_DATA = "[^;].*"; static final String RR_COMMENT = "(;.*)*"; static final String RR_REGEX_TTL_IN = @@ -60,6 +63,22 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + "$"); } + @Override + @SneakyThrows + public List validateContext(final HsHostingAssetEntity assetEntity) { + final var result = super.validateContext(assetEntity); + + // TODO.spec: define which checks should get raised to error level + final var namedCheckZone = new SystemProcess("named-checkzone", assetEntity.getIdentifier()); + if (namedCheckZone.execute(toZonefileString(assetEntity)) != 0) { + // yes, named-checkzone writes error messages to stdout + stream(namedCheckZone.getStdOut().split("\n")) + .map(line -> line.replaceAll(" stream-0x[0-9a-f:]+", "")) + .forEach(result::add); + } + return result; + } + String toZonefileString(final HsHostingAssetEntity assetEntity) { return """ $ORIGIN {domain}. diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index ae332f07..d5b684a7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; import java.util.Map; import static java.util.Map.entry; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_COMMENT; @@ -35,6 +34,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { .identifier("example.org") .config(Map.ofEntries( entry("user-RR", Array.of( + "@ 1814400 IN XXX example.org. root.example.org ( 1234 10800 900 604800 86400 )", "www IN CNAME example.com. ; www.example.com is an alias for example.com", "test1 IN 1h30m CNAME example.com.", "test2 1h30m IN CNAME example.com.", @@ -94,7 +94,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { "{type=boolean, propertyName=auto-WILDCARD-AAAA-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-DKIM-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-SPF-RR, defaultValue=true}", - "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+([a-z0-9\\.-]+|\\([^\\)]*\\)|\"[^\"]*\")\\s*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+([a-z0-9\\.-]+|\\([^\\)]*\\)|\"[^\"]*\")\\s*(;.*)*], required=true}}" + "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*], required=true}}" ); } @@ -149,7 +149,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { @Test void generatesZonefile() { // given - final var givenEntity = validEntityBuilder().identifier("example.org").build(); + final var givenEntity = validEntityBuilder().build(); final var validator = (HsDomainDnsSetupHostingAssetValidator) HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); // when @@ -164,10 +164,46 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { @ 1814400 IN SOA example.org. root.example.org ( 1999010100 10800 900 604800 86400 ) @ IN NS ns + @ 1814400 IN XXX example.org. root.example.org ( 1234 10800 900 604800 86400 ) www IN CNAME example.com. ; www.example.com is an alias for example.com test1 IN 1h30m CNAME example.com. test2 1h30m IN CNAME example.com. ns IN A 192.0.2.2; IPv4 address for ns.example.com """); } + + @Test + void acceptsValidEntity() { + // given + final var givenEntity = validEntityBuilder().build(); + final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var errors = validator.validateEntity(givenEntity); + + // then + assertThat(errors).isEmpty(); + } + + @Test + void rejectsInvalidEntity() { + // given + final var givenEntity = validEntityBuilder().config(Map.ofEntries( + entry("user-RR", Array.of( + "example.org. 1814400 IN SOA example.org. root.example.org (1234 10800 900 604800 86400)" + )) + )) + .build(); + final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var errors = validator.validateContext(givenEntity); + + // then + assertThat(errors).containsExactlyInAnyOrder( + "dns_master_load: example.org: multiple RRs of singleton type", + "zone example.org/IN: loading from master file (null) failed: multiple RRs of singleton type", + "zone example.org/IN: not loaded due to errors." + ); + } }