call named-checkzone in validateContext

This commit is contained in:
Michael Hoennig 2024-07-04 14:02:19 +02:00
parent 93774aebe3
commit fe3cbd50fb
2 changed files with 60 additions and 5 deletions

View File

@ -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<String> 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}.

View File

@ -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."
);
}
}