generate almost complete Bind zonefile

This commit is contained in:
Michael Hoennig 2024-08-07 17:17:35 +02:00
parent becca99e0f
commit 6e5d51384b
10 changed files with 280 additions and 108 deletions

View File

@ -18,18 +18,18 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator { class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
// according to RFC 1035 (section 5) and RFC 1034 // according to RFC 1035 (section 5) and RFC 1034
static final String RR_REGEX_NAME = "(\\*\\.)?([a-z0-9\\._-]+|@)\\s+"; static final String RR_REGEX_NAME = "(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+";
static final String RR_REGEX_TTL = "(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*"; static final String RR_REGEX_TTL = "(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?";
static final String RR_REGEX_IN = "IN\\s+"; // record class IN for Internet static final String RR_REGEX_IN = "[iI][nN][ \t]+"; // record class IN for Internet
static final String RR_RECORD_TYPE = "[A-Z]+\\s+"; static final String RR_RECORD_TYPE = "[a-zA-Z]+[ \t]+";
static final String RR_RECORD_DATA = "[([^;]+)|(\".*\")|(\\(.*\\))]\\s*"; static final String RR_RECORD_DATA = "(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*";
static final String RR_COMMENT = "(;.*)?"; static final String RR_COMMENT = "(;.*)?";
static final String RR_REGEX_TTL_IN = static final String RR_REGEX_TTL_IN =
RR_REGEX_NAME + RR_REGEX_TTL + RR_REGEX_IN + RR_RECORD_TYPE + ".*"; // RR_RECORD_DATA + RR_COMMENT; FIXME RR_REGEX_NAME + RR_REGEX_TTL + RR_REGEX_IN + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
static final String RR_REGEX_IN_TTL = static final String RR_REGEX_IN_TTL =
RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + ".*"; // RR_RECORD_DATA + RR_COMMENT; FIXME RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
public static final String IDENTIFIER_SUFFIX = "|DNS"; public static final String IDENTIFIER_SUFFIX = "|DNS";
HsDomainDnsSetupHostingAssetValidator() { HsDomainDnsSetupHostingAssetValidator() {
@ -44,14 +44,13 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
booleanProperty("auto-A-RR").withDefault(true), booleanProperty("auto-A-RR").withDefault(true),
booleanProperty("auto-AAAA-RR").withDefault(true), booleanProperty("auto-AAAA-RR").withDefault(true),
booleanProperty("auto-MAILSERVICES-RR").withDefault(true), booleanProperty("auto-MAILSERVICES-RR").withDefault(true),
booleanProperty("auto-AUTOCONFIG-RR").withDefault(true), // TODO.spec: does that already exist? booleanProperty("auto-AUTOCONFIG-RR").withDefault(true),
booleanProperty("auto-AUTODISCOVER-RR").withDefault(true), booleanProperty("auto-AUTODISCOVER-RR").withDefault(true),
booleanProperty("auto-DKIM-RR").withDefault(true), booleanProperty("auto-DKIM-RR").withDefault(true),
booleanProperty("auto-SPF-RR").withDefault(true), booleanProperty("auto-SPF-RR").withDefault(true),
booleanProperty("auto-WILDCARD-MX-RR").withDefault(true), booleanProperty("auto-WILDCARD-MX-RR").withDefault(true),
booleanProperty("auto-WILDCARD-A-RR").withDefault(true), booleanProperty("auto-WILDCARD-A-RR").withDefault(true),
booleanProperty("auto-WILDCARD-AAAA-RR").withDefault(true), booleanProperty("auto-WILDCARD-AAAA-RR").withDefault(true),
booleanProperty("auto-WILDCARD-DKIM-RR").withDefault(true), // TODO.spec: check, if that really works
booleanProperty("auto-WILDCARD-SPF-RR").withDefault(true), booleanProperty("auto-WILDCARD-SPF-RR").withDefault(true),
arrayOf( arrayOf(
stringProperty("user-RR").matchesRegEx(RR_REGEX_TTL_IN, RR_REGEX_IN_TTL).required() stringProperty("user-RR").matchesRegEx(RR_REGEX_TTL_IN, RR_REGEX_IN_TTL).required()
@ -82,22 +81,30 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
if (namedCheckZone.execute(zonefileString) != 0) { if (namedCheckZone.execute(zonefileString) != 0) {
// yes, named-checkzone writes error messages to stdout // yes, named-checkzone writes error messages to stdout
stream(namedCheckZone.getStdOut().split("\n")) stream(namedCheckZone.getStdOut().split("\n"))
.map(line -> line.replaceAll(" stream-0x[0-9a-f:]+", "")) .map(line -> line.replaceAll(" stream-0x[0-9a-f]+:", "line "))
.map(line -> "[" + assetEntity.getIdentifier() + "] " + line)
.forEach(result::add); .forEach(result::add);
} }
return result; return result;
} }
String toZonefileString(final HsHostingAsset assetEntity) { String toZonefileString(final HsHostingAsset assetEntity) {
// TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack // TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack, with proper IP-numbers etc.
return """ return """
$ORIGIN {domain}.
$TTL {ttl} $TTL {ttl}
{auto-SOA} {auto-SOA}
{auto-NS-RR} {auto-NS-RR}
{auto-MX-RR}
{auto-A-RR} {auto-A-RR}
{auto-AAAA-RR} {auto-AAAA-RR}
{auto-DKIM-RR}
{auto-SPF-RR}
{auto-WILDCARD-MX-RR}
{auto-WILDCARD-A-RR}
{auto-WILDCARD-AAAA-RR}
{auto-WILDCARD-SPF-RR}
{userRRs} {userRRs}
""" """
@ -110,17 +117,52 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
1H ; retry (>=1800) 1H ; retry (>=1800)
1W ; expire 1W ; expire
1H ; minimum 1H ; minimum
) )
""" """
: "" : "; no auto-SOA"
) )
.replace("{auto-NS-RR}", """ .replace("{auto-NS-RR}", assetEntity.getDirectValue("auto-NS-RR", Boolean.class, true)
{domain}. IN NS dns1.hostsharing.net. ? """
{domain}. IN NS dns2.hostsharing.net. {domain}. IN NS dns1.hostsharing.net.
{domain}. IN NS dns3.hostsharing.net. {domain}. IN NS dns2.hostsharing.net.
""") {domain}. IN NS dns3.hostsharing.net.
.replace("{auto-A-RR}", "{domain}. IN A 83.223.95.160") // arbitrary IP-number """
.replace("{auto-AAAA-RR}", "{domain}. IN AAAA 2a01:37:1000::53df:5fa0:0") // arbitrary IP-number : "; no auto-NS-RR")
.replace("{auto-MX-RR}", assetEntity.getDirectValue("auto-MX-RR", Boolean.class, true)
? """
{domain}. IN MX 30 mailin1.hostsharing.net.
{domain}. IN MX 30 mailin2.hostsharing.net.
{domain}. IN MX 30 mailin3.hostsharing.net.
"""
: "; no auto-MX-RR")
.replace("{auto-A-RR}", assetEntity.getDirectValue("auto-A-RR", Boolean.class, true)
? "{domain}. IN A 83.223.95.160" // arbitrary IP-number
: "; no auto-A-RR")
.replace("{auto-AAAA-RR}", assetEntity.getDirectValue("auto-AAA-RR", Boolean.class, true)
? "{domain}. IN AAAA 2a01:37:1000::53df:5fa0:0" // arbitrary IP-number
: "; no auto-AAAA-RR")
.replace("{auto-DKIM-RR}", assetEntity.getDirectValue("auto-DKIM-RR", Boolean.class, true)
? "default._domainkey 21600 IN TXT \"v=DKIM1; h=sha256; k=rsa; s=email; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmdM9d15bqe94zbHVcKKpUF875XoCWHKRap/sG3NJZ9xZ/BjfGXmqoEYeFNpX3CB7pOXhH5naq4N+6gTjArTviAiVThHXyebhrxaf1dVS4IUC6raTEyQrWPZUf7ZxXmcCYvOdV4jIQ8GRfxwxqibIJcmMiufXTLIgRUif5uaTgFwIDAQAB\""
: "; no auto-DKIM-RR")
.replace("{auto-SPF-RR}", assetEntity.getDirectValue("auto-SPF-RR", Boolean.class, true)
? "{domain}. IN TXT \"v=spf1 include:spf.hostsharing.net ?all\""
: "; no auto-SPF-RR")
.replace("{auto-WILDCARD-MX-RR}", assetEntity.getDirectValue("auto-SPF-RR", Boolean.class, true)
? """
*.{domain}. IN MX 30 mailin1.hostsharing.net.
*.{domain}. IN MX 30 mailin1.hostsharing.net.
*.{domain}. IN MX 30 mailin1.hostsharing.net.
"""
: "; no auto-WILDCARD-MX-RR")
.replace("{auto-WILDCARD-A-RR}", assetEntity.getDirectValue("auto-WILDCARD-A-RR", Boolean.class, true)
? "*.{domain}. IN A 83.223.95.160" // arbitrary IP-number
: "; no auto-WILDCARD-A-RR")
.replace("{auto-WILDCARD-AAAA-RR}", assetEntity.getDirectValue("auto-WILDCARD-AAAA-RR", Boolean.class, true)
? "*.{domain}. IN AAAA 2a01:37:1000::53df:5fa0:0" // arbitrary IP-number
: "; no auto-WILDCARD-AAAA-RR")
.replace("{auto-WILDCARD-SPF-RR}", assetEntity.getDirectValue("auto-WILDCARD-SPF-RR", Boolean.class, true)
? "*.{domain}. IN TXT \"v=spf1 include:spf.hostsharing.net ?all\""
: "; no auto-WILDCARD-SPF-RR")
.replace("{domain}", fqdn(assetEntity)) .replace("{domain}", fqdn(assetEntity))
.replace("{userRRs}", getPropertyValues(assetEntity, "user-RR")); .replace("{userRRs}", getPropertyValues(assetEntity, "user-RR"));
} }

View File

@ -14,7 +14,7 @@ class HsDomainHttpSetupHostingAssetValidator extends HostingAssetEntityValidator
public static final String IDENTIFIER_SUFFIX = "|HTTP"; public static final String IDENTIFIER_SUFFIX = "|HTTP";
public static final String FILESYSTEM_PATH = "^/.*"; public static final String FILESYSTEM_PATH = "^/.*";
public static final String PARTIAL_DOMAIN_NAME_REGEX = "(?!-)[A-Za-z0-9-]{1,63}(?<!-)"; public static final String SUBDOMAIN_NAME_REGEX = "(\\*|(?!-)[A-Za-z0-9-]{1,63}(?<!-))";
HsDomainHttpSetupHostingAssetValidator() { HsDomainHttpSetupHostingAssetValidator() {
super( super(
@ -37,7 +37,7 @@ class HsDomainHttpSetupHostingAssetValidator extends HostingAssetEntityValidator
stringProperty("passenger-python").matchesRegEx(FILESYSTEM_PATH).provided("/usr/bin/python3").withDefault("/usr/bin/python3"), stringProperty("passenger-python").matchesRegEx(FILESYSTEM_PATH).provided("/usr/bin/python3").withDefault("/usr/bin/python3"),
stringProperty("passenger-ruby").matchesRegEx(FILESYSTEM_PATH).provided("/usr/bin/ruby").withDefault("/usr/bin/ruby"), stringProperty("passenger-ruby").matchesRegEx(FILESYSTEM_PATH).provided("/usr/bin/ruby").withDefault("/usr/bin/ruby"),
arrayOf( arrayOf(
stringProperty("subdomains").matchesRegEx(PARTIAL_DOMAIN_NAME_REGEX).required() stringProperty("subdomains").matchesRegEx(SUBDOMAIN_NAME_REGEX).required()
).optional()); ).optional());
} }

View File

@ -145,4 +145,8 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
} }
return ""; return "";
} }
public ValidatableProperty<?, ?> getProperty(final String propertyName) {
return stream(propertyValidators).filter(pv -> pv.propertyName().equals(propertyName)).findFirst().orElse(null);
}
} }

View File

@ -35,13 +35,27 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
.assignedToAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .assignedToAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
.identifier("example.org|DNS") .identifier("example.org|DNS")
.config(Map.ofEntries( .config(Map.ofEntries(
entry("TTL", 21600),
entry("auto-SOA", true),
entry("auto-NS-RR", true),
entry("auto-MX-RR", true),
entry("auto-A-RR", true),
entry("auto-AAAA-RR", true),
entry("auto-MAILSERVICES-RR", true),
entry("auto-AUTOCONFIG-RR", true),
entry("auto-AUTODISCOVER-RR", true),
entry("auto-DKIM-RR", true),
entry("auto-SPF-RR", true),
entry("auto-WILDCARD-MX-RR", true),
entry("auto-WILDCARD-A-RR", true),
entry("auto-WILDCARD-AAAA-RR", true),
entry("auto-WILDCARD-SPF-RR", true),
entry("user-RR", Array.of( 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", "www IN CNAME example.com. ; www.example.com is an alias for example.com",
"test1 IN 1h30m CNAME example.com.", "test1 IN 1h30m CNAME example.com.",
"test2 1h30m IN CNAME example.com.", "test2 1h30m IN CNAME example.com.",
"ns IN A 192.0.2.2; IPv4 address for ns.example.com", "ns IN A 192.0.2.2; IPv4 address for ns.example.com",
"key1._domainkey.example.org. 21600 IN TXT \"v=DKIM1; k=rsa; t=s; h=sha256; s=email; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKzG+6ZiD7p60PFZ/qxmKNmP3AO3cIszXYyfvHn/MkyGx0vXhSolAheZtK6+g/h3m6McdPR6kHywcPuQRAPbcVh+SpPAorWe18VLdMcW4D6KxbMjQipRw1cZ4PjglGgcvsT42IAVQMFlEGl6KutmDkZebJNHlrFj38FcwD1wjL0wIDAQAB\"") "_acme-challenge.PAULCHEN-VS.core.example.org. 60 IN CNAME _acme-challenge.core.example.org.acme-pki.de.")
) )
)); ));
} }
@ -67,9 +81,8 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
"{type=boolean, propertyName=auto-WILDCARD-MX-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-MX-RR, defaultValue=true}",
"{type=boolean, propertyName=auto-WILDCARD-A-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-A-RR, defaultValue=true}",
"{type=boolean, propertyName=auto-WILDCARD-AAAA-RR, defaultValue=true}", "{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=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+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+(\".*\")?([^;]*)?(;.*)?], required=true}}" "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?[iI][nN][ \t]+[a-zA-Z]+[ \t]+(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*(;.*)?, (\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+[iI][nN][ \t]+(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?[a-zA-Z]+[ \t]+(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*(;.*)?], required=true}}"
); );
} }
@ -136,7 +149,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
} }
@Test @Test
void acceptsValidEntity() { void acceptsValidEntityItself() {
// given // given
final var givenEntity = validEntityBuilder().build(); final var givenEntity = validEntityBuilder().build();
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
@ -148,6 +161,19 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
assertThat(errors).isEmpty(); assertThat(errors).isEmpty();
} }
@Test
void acceptsValidEntityInContext() {
// given
final var givenEntity = validEntityBuilder().build();
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
// when
final var errors = validator.validateContext(givenEntity);
// then
assertThat(errors).isEmpty();
}
@Test @Test
void rejectsInvalidProperties() { void rejectsInvalidProperties() {
// given // given
@ -167,37 +193,56 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'DOMAIN_DNS_SETUP:example.org|DNS.config.TTL' is expected to be of type Integer, but is of type String", "'DOMAIN_DNS_SETUP:example.org|DNS.config.TTL' is expected to be of type Integer, but is of type String",
"'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [(\\*\\.)?([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+(\".*\")?([^;]*)?(;.*)?] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?[iI][nN][ \t]+[a-zA-Z]+[ \t]+(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*(;.*)?, (\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+[iI][nN][ \t]+(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?[a-zA-Z]+[ \t]+(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*(;.*)?] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any",
"'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [(\\*\\.)?([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+(\".*\")?([^;]*)?(;.*)?] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?[iI][nN][ \t]+[a-zA-Z]+[ \t]+(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*(;.*)?, (\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+[iI][nN][ \t]+(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?[a-zA-Z]+[ \t]+(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*(;.*)?] but 'www BAD1 Record-Class missing / not enough columns' does not match any");
} }
@Test @Test
void validStringMatchesRegEx() { void validNameMatchesRegEx() {
assertThat("@ ").matches(RR_REGEX_NAME); assertThat("@ ").matches(RR_REGEX_NAME);
assertThat("ns ").matches(RR_REGEX_NAME); assertThat("ns ").matches(RR_REGEX_NAME);
assertThat("example.com. ").matches(RR_REGEX_NAME); assertThat("example.com. ").matches(RR_REGEX_NAME);
assertThat("example.ORG. ").matches(RR_REGEX_NAME);
}
@Test
void validTtlMatchesRegEx() {
assertThat("12400 ").matches(RR_REGEX_TTL); assertThat("12400 ").matches(RR_REGEX_TTL);
assertThat("12400\t\t ").matches(RR_REGEX_TTL); assertThat("12400\t\t ").matches(RR_REGEX_TTL);
assertThat("12400 \t\t").matches(RR_REGEX_TTL); assertThat("12400 \t\t").matches(RR_REGEX_TTL);
assertThat("1h30m ").matches(RR_REGEX_TTL); assertThat("1h30m ").matches(RR_REGEX_TTL);
assertThat("30m ").matches(RR_REGEX_TTL); assertThat("30m ").matches(RR_REGEX_TTL);
}
@Test
void validInMatchesRegEx() {
assertThat("in ").matches(RR_REGEX_IN);
assertThat("IN ").matches(RR_REGEX_IN); assertThat("IN ").matches(RR_REGEX_IN);
assertThat("IN\t\t ").matches(RR_REGEX_IN); assertThat("IN\t\t ").matches(RR_REGEX_IN);
assertThat("IN \t\t").matches(RR_REGEX_IN); assertThat("IN \t\t").matches(RR_REGEX_IN);
}
@Test
void validRecordTypeMatchesRegEx() {
assertThat("a ").matches(RR_RECORD_TYPE);
assertThat("CNAME ").matches(RR_RECORD_TYPE); assertThat("CNAME ").matches(RR_RECORD_TYPE);
assertThat("CNAME\t\t ").matches(RR_RECORD_TYPE); assertThat("CNAME\t\t ").matches(RR_RECORD_TYPE);
assertThat("CNAME \t\t").matches(RR_RECORD_TYPE); assertThat("CNAME \t\t").matches(RR_RECORD_TYPE);
}
@Test
void validRecordDataMatchesRegEx() {
assertThat("example.com.").matches(RR_RECORD_DATA); assertThat("example.com.").matches(RR_RECORD_DATA);
assertThat("example.com. ").matches(RR_RECORD_DATA); assertThat("example.com. ").matches(RR_RECORD_DATA);
assertThat("123.123.123.123").matches(RR_RECORD_DATA); assertThat("123.123.123.123").matches(RR_RECORD_DATA);
assertThat("123.123.123.123 ").matches(RR_RECORD_DATA); assertThat("123.123.123.123 ").matches(RR_RECORD_DATA);
assertThat("_acme-challenge.core.example.org.acme-pki.de.").matches(RR_RECORD_DATA);
assertThat("(some more complex argument in parenthesis)").matches(RR_RECORD_DATA); assertThat("(some more complex argument in parenthesis)").matches(RR_RECORD_DATA);
assertThat("\"some more complex argument; including a semicolon\"").matches(RR_RECORD_DATA); assertThat("\"some more complex argument; including a semicolon\"").matches(RR_RECORD_DATA);
}
@Test
void validCommentMatchesRegEx() {
assertThat("; whatever ; \" really anything").matches(RR_COMMENT); assertThat("; whatever ; \" really anything").matches(RR_COMMENT);
} }
@ -212,23 +257,42 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// then // then
assertThat(zonefile).isEqualTo(""" assertThat(zonefile).isEqualTo("""
$ORIGIN example.org. $TTL 21600
$TTL 43200
example.org. IN SOA h00.hostsharing.net. hostmaster.hostsharing.net. (
1303649373 ; serial secs since Jan 1 1970
6H ; refresh (>=10000)
1H ; retry (>=1800)
1W ; expire
1H ; minimum
)
example.org. IN NS dns1.hostsharing.net. example.org. IN NS dns1.hostsharing.net.
example.org. IN NS dns2.hostsharing.net. example.org. IN NS dns2.hostsharing.net.
example.org. IN NS dns3.hostsharing.net. example.org. IN NS dns3.hostsharing.net.
example.org. IN MX 30 mailin1.hostsharing.net.
example.org. IN MX 30 mailin2.hostsharing.net.
example.org. IN MX 30 mailin3.hostsharing.net.
example.org. IN A 83.223.95.160 example.org. IN A 83.223.95.160
example.org. IN AAAA 2a01:37:1000::53df:5fa0:0 example.org. IN AAAA 2a01:37:1000::53df:5fa0:0
default._domainkey 21600 IN TXT "v=DKIM1; h=sha256; k=rsa; s=email; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmdM9d15bqe94zbHVcKKpUF875XoCWHKRap/sG3NJZ9xZ/BjfGXmqoEYeFNpX3CB7pOXhH5naq4N+6gTjArTviAiVThHXyebhrxaf1dVS4IUC6raTEyQrWPZUf7ZxXmcCYvOdV4jIQ8GRfxwxqibIJcmMiufXTLIgRUif5uaTgFwIDAQAB"
@ 1814400 IN XXX example.org. root.example.org ( 1234 10800 900 604800 86400 ) example.org. IN TXT "v=spf1 include:spf.hostsharing.net ?all"
*.example.org. IN MX 30 mailin1.hostsharing.net.
*.example.org. IN MX 30 mailin1.hostsharing.net.
*.example.org. IN MX 30 mailin1.hostsharing.net.
*.example.org. IN A 83.223.95.160
*.example.org. IN AAAA 2a01:37:1000::53df:5fa0:0
*.example.org. IN TXT "v=spf1 include:spf.hostsharing.net ?all"
www IN CNAME example.com. ; www.example.com is an alias for example.com www IN CNAME example.com. ; www.example.com is an alias for example.com
test1 IN 1h30m CNAME example.com. test1 IN 1h30m CNAME example.com.
test2 1h30m IN CNAME example.com. test2 1h30m IN CNAME example.com.
ns IN A 192.0.2.2; IPv4 address for ns.example.com ns IN A 192.0.2.2; IPv4 address for ns.example.com
key1._domainkey.example.org. 21600 IN TXT "v=DKIM1; k=rsa; t=s; h=sha256; s=email; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKzG+6ZiD7p60PFZ/qxmKNmP3AO3cIszXYyfvHn/MkyGx0vXhSolAheZtK6+g/h3m6McdPR6kHywcPuQRAPbcVh+SpPAorWe18VLdMcW4D6KxbMjQipRw1cZ4PjglGgcvsT42IAVQMFlEGl6KutmDkZebJNHlrFj38FcwD1wjL0wIDAQAB" _acme-challenge.PAULCHEN-VS.core.example.org. 60 IN CNAME _acme-challenge.core.example.org.acme-pki.de.
"""); """);
} }
@ -249,9 +313,9 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// then // then
assertThat(errors).containsExactlyInAnyOrder( assertThat(errors).containsExactlyInAnyOrder(
"dns_master_load: example.org: multiple RRs of singleton type", "[example.org|DNS] dns_master_load:line 26: example.org: multiple RRs of singleton type",
"zone example.org/IN: loading from master file (null) failed: multiple RRs of singleton type", "[example.org|DNS] zone example.org/IN: loading from master file (null) failed: multiple RRs of singleton type",
"zone example.org/IN: not loaded due to errors." "[example.org|DNS] zone example.org/IN: not loaded due to errors."
); );
} }
} }

View File

@ -310,11 +310,15 @@ public class CsvDataImport extends ContextBasedTest {
try { try {
assertion.run(); assertion.run();
} catch (final AssertionError exc) { } catch (final AssertionError exc) {
errors.add(exc.toString()); logError(exc.getMessage());
} }
} }
void logErrors() { public static void logError(final String error) {
errors.add(error);
}
protected final void logErrors() {
final var errorsToLog = new ArrayList<>(errors); final var errorsToLog = new ArrayList<>(errors);
errors.clear(); errors.clear();
assertThat(errorsToLog).isEmpty(); assertThat(errorsToLog).isEmpty();

View File

@ -12,6 +12,7 @@ import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityV
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
@ -139,6 +140,8 @@ public class ImportHostingAssets extends ImportOfficeData {
static Map<String, HsHostingAssetRealEntity> dbUsersByEngineAndName = new WriteOnceMap<>(); static Map<String, HsHostingAssetRealEntity> dbUsersByEngineAndName = new WriteOnceMap<>();
static Map<String, HsHostingAssetRealEntity> domainSetupsByName = new WriteOnceMap<>(); static Map<String, HsHostingAssetRealEntity> domainSetupsByName = new WriteOnceMap<>();
final ObjectMapper jsonMapper = new ObjectMapper();
@Test @Test
@Order(11010) @Order(11010)
void createBookingProjects() { void createBookingProjects() {
@ -329,7 +332,7 @@ public class ImportHostingAssets extends ImportOfficeData {
4100824=HsHostingAssetRealEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, {"HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}), 4100824=HsHostingAssetRealEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, {"HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}),
4167846=HsHostingAssetRealEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, {"HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}), 4167846=HsHostingAssetRealEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, {"HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}),
4169546=HsHostingAssetRealEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), 4169546=HsHostingAssetRealEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}),
4169596=HsHostingAssetRealEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) 4169596=HsHostingAssetRealEntity(UNIX_USER, dph00-dph, Domain admin, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594})
} }
"""); """);
} }
@ -469,13 +472,17 @@ public class ImportHostingAssets extends ImportOfficeData {
@Order(16020) @Order(16020)
void importZonenfiles() { void importZonenfiles() {
final var reflections = new Reflections(MIGRATION_DATA_PATH + "/hosting/zonefiles", new ResourcesScanner()); final var reflections = new Reflections(MIGRATION_DATA_PATH + "/hosting/zonefiles", new ResourcesScanner());
final var zonefileFiles = reflections.getResources(Pattern.compile(".*\\.json")); final var zonefileFiles = reflections.getResources(Pattern.compile(".*\\.json")).stream().sorted().toList();
zonefileFiles.forEach(zonenfileName -> { zonefileFiles.forEach(zonenfileName -> {
System.out.println("Processing zonenfile: " + zonenfileName); System.out.println("Processing zonenfile: " + zonenfileName);
importZonefiles(resourceAsString(zonenfileName)); importZonefiles(vmName(zonenfileName), resourceAsString(zonenfileName));
}); });
} }
private String vmName(final String zonenfileName) {
return zonenfileName.substring(zonenfileName.length()-"vm0000.json".length()).substring(0, 6);
}
@Test @Test
@Order(16019) @Order(16019)
void verifyDomains() { void verifyDomains() {
@ -507,15 +514,15 @@ public class ImportHostingAssets extends ImportOfficeData {
11004600=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, waera.de|DNS, DNS-Setup für waera.de, DOMAIN_SETUP:waera.de, MANAGED_WEBSPACE:mim00), 11004600=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, waera.de|DNS, DNS-Setup für waera.de, DOMAIN_SETUP:waera.de, MANAGED_WEBSPACE:mim00),
11004604=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, xn--wra-qla.de|DNS, DNS-Setup für wära.de, DOMAIN_SETUP:xn--wra-qla.de, MANAGED_WEBSPACE:mim00), 11004604=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, xn--wra-qla.de|DNS, DNS-Setup für wära.de, DOMAIN_SETUP:xn--wra-qla.de, MANAGED_WEBSPACE:mim00),
11027662=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, dph-netzwerk.de|DNS, DNS-Setup für dph-netzwerk.de, DOMAIN_SETUP:dph-netzwerk.de, MANAGED_WEBSPACE:dph00), 11027662=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, dph-netzwerk.de|DNS, DNS-Setup für dph-netzwerk.de, DOMAIN_SETUP:dph-netzwerk.de, MANAGED_WEBSPACE:dph00),
12004531=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, l-u-g.org|HTTP, HTTP-Setup für l-u-g.org, DOMAIN_SETUP:l-u-g.org, UNIX_USER:lug00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004531=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, l-u-g.org|HTTP, HTTP-Setup für l-u-g.org, DOMAIN_SETUP:l-u-g.org, UNIX_USER:lug00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
12004532=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, linuxfanboysngirls.de|HTTP, HTTP-Setup für linuxfanboysngirls.de, DOMAIN_SETUP:linuxfanboysngirls.de, UNIX_USER:lug00-wla.2, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004532=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, linuxfanboysngirls.de|HTTP, HTTP-Setup für linuxfanboysngirls.de, DOMAIN_SETUP:linuxfanboysngirls.de, UNIX_USER:lug00-wla.2, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
12004534=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, lug-mars.de|HTTP, HTTP-Setup für lug-mars.de, DOMAIN_SETUP:lug-mars.de, UNIX_USER:lug00-wla.2, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004534=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, lug-mars.de|HTTP, HTTP-Setup für lug-mars.de, DOMAIN_SETUP:lug-mars.de, UNIX_USER:lug00-wla.2, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "www" ]}),
12004581=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, 1981.ist-im-netz.de|HTTP, HTTP-Setup für 1981.ist-im-netz.de, DOMAIN_SETUP:1981.ist-im-netz.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004581=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, 1981.ist-im-netz.de|HTTP, HTTP-Setup für 1981.ist-im-netz.de, DOMAIN_SETUP:1981.ist-im-netz.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
12004587=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, mellis.de|HTTP, HTTP-Setup für mellis.de, DOMAIN_SETUP:mellis.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": false, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004587=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, mellis.de|HTTP, HTTP-Setup für mellis.de, DOMAIN_SETUP:mellis.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": false, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "www", "michael", "test", "photos", "static", "input" ]}),
12004589=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, ist-im-netz.de|HTTP, HTTP-Setup für ist-im-netz.de, DOMAIN_SETUP:ist-im-netz.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": false, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004589=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, ist-im-netz.de|HTTP, HTTP-Setup für ist-im-netz.de, DOMAIN_SETUP:ist-im-netz.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": false, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
12004600=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, waera.de|HTTP, HTTP-Setup für waera.de, DOMAIN_SETUP:waera.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004600=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, waera.de|HTTP, HTTP-Setup für waera.de, DOMAIN_SETUP:waera.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
12004604=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, xn--wra-qla.de|HTTP, HTTP-Setup für wära.de, DOMAIN_SETUP:xn--wra-qla.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12004604=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, xn--wra-qla.de|HTTP, HTTP-Setup für wära.de, DOMAIN_SETUP:xn--wra-qla.de, UNIX_USER:mim00, {"autoconfig": false, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": false, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
12027662=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, dph-netzwerk.de|HTTP, HTTP-Setup für dph-netzwerk.de, DOMAIN_SETUP:dph-netzwerk.de, UNIX_USER:dph00-uph, {"autoconfig": true, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby"}), 12027662=HsHostingAssetRealEntity(DOMAIN_HTTP_SETUP, dph-netzwerk.de|HTTP, HTTP-Setup für dph-netzwerk.de, DOMAIN_SETUP:dph-netzwerk.de, UNIX_USER:dph00-dph, {"autoconfig": true, "cgi": true, "fastcgi": true, "fcgi-php-bin": "/usr/lib/cgi-bin/php", "greylisting": true, "htdocsfallback": true, "includes": true, "indexes": true, "letsencrypt": true, "multiviews": true, "passenger": true, "passenger-errorpage": false, "passenger-nodejs": "/usr/bin/node", "passenger-python": "/usr/bin/python3", "passenger-ruby": "/usr/bin/ruby", "subdomains": [ "*" ]}),
13004531=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, l-u-g.org|MBOX, E-Mail-Empfang-Setup für l-u-g.org, DOMAIN_SETUP:l-u-g.org, MANAGED_WEBSPACE:lug00), 13004531=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, l-u-g.org|MBOX, E-Mail-Empfang-Setup für l-u-g.org, DOMAIN_SETUP:l-u-g.org, MANAGED_WEBSPACE:lug00),
13004532=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, linuxfanboysngirls.de|MBOX, E-Mail-Empfang-Setup für linuxfanboysngirls.de, DOMAIN_SETUP:linuxfanboysngirls.de, MANAGED_WEBSPACE:lug00), 13004532=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, linuxfanboysngirls.de|MBOX, E-Mail-Empfang-Setup für linuxfanboysngirls.de, DOMAIN_SETUP:linuxfanboysngirls.de, MANAGED_WEBSPACE:lug00),
13004534=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, lug-mars.de|MBOX, E-Mail-Empfang-Setup für lug-mars.de, DOMAIN_SETUP:lug-mars.de, MANAGED_WEBSPACE:lug00), 13004534=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, lug-mars.de|MBOX, E-Mail-Empfang-Setup für lug-mars.de, DOMAIN_SETUP:lug-mars.de, MANAGED_WEBSPACE:lug00),
@ -736,7 +743,7 @@ public class ImportHostingAssets extends ImportOfficeData {
4100824=HsHostingAssetRealEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 10000}), 4100824=HsHostingAssetRealEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 10000}),
4167846=HsHostingAssetRealEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 110568}), 4167846=HsHostingAssetRealEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 110568}),
4169546=HsHostingAssetRealEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110593}), 4169546=HsHostingAssetRealEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110593}),
4169596=HsHostingAssetRealEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110594}) 4169596=HsHostingAssetRealEntity(UNIX_USER, dph00-dph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110594})
} }
"""); """);
} }
@ -760,9 +767,9 @@ public class ImportHostingAssets extends ImportOfficeData {
// ============================================================================================ // ============================================================================================
@Test @Test
@Order(99999) @Order(19999)
void logErrors() { void logErrorsAfterPersistingHostingAssets() {
super.logErrors(); logErrors();
} }
private void persistRecursively(final Integer key, final HsBookingItemEntity bi) { private void persistRecursively(final Integer key, final HsBookingItemEntity bi) {
@ -1177,6 +1184,8 @@ public class ImportHostingAssets extends ImportOfficeData {
} }
private void importDomains(final String[] header, final List<String[]> records) { private void importDomains(final String[] header, final List<String[]> records) {
final var httpDomainSetupValidator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_HTTP_SETUP);
final var columns = new Columns(header); final var columns = new Columns(header);
records.stream() records.stream()
.map(this::trimAll) .map(this::trimAll)
@ -1187,12 +1196,6 @@ public class ImportHostingAssets extends ImportOfficeData {
// final var domain_since = rec.getString("domain_since"); // final var domain_since = rec.getString("domain_since");
// final var domain_dns_master = rec.getString("domain_dns_master"); // final var domain_dns_master = rec.getString("domain_dns_master");
final var owner_id = rec.getInteger("domain_owner"); final var owner_id = rec.getInteger("domain_owner");
// FIXME: missing properties
final var valid_subdomain_names = rec.getString("valid_subdomain_names");
final var passenger_python = rec.getString("passenger_python");
final var passenger_nodejs = rec.getString("passenger_nodejs");
final var passenger_ruby = rec.getString("passenger_ruby");
final var fcgi_php_bin = rec.getString("fcgi_php_bin");
final var domainoptions = rec.getString("domainoptions"); final var domainoptions = rec.getString("domainoptions");
// Domain Setup // Domain Setup
@ -1244,10 +1247,16 @@ public class ImportHostingAssets extends ImportOfficeData {
entry("includes", options.contains("includes")), entry("includes", options.contains("includes")),
entry("letsencrypt", options.contains("letsencrypt")), entry("letsencrypt", options.contains("letsencrypt")),
entry("multiviews", options.contains("multiviews")), entry("multiviews", options.contains("multiviews")),
entry("fcgi-php-bin", rec.getString("fcgi_php_bin")), entry("subdomains", withDefault(rec.getString("valid_subdomain_names"), "*")
entry("passenger-nodejs", rec.getString("passenger_nodejs")), .split(",")),
entry("passenger-python", rec.getString("passenger_python")), entry("fcgi-php-bin", withDefault(rec.getString("fcgi_php_bin"),
entry("passenger-ruby", rec.getString("passenger_ruby")) httpDomainSetupValidator.getProperty("fcgi-php-bin").defaultValue() )),
entry("passenger-nodejs", withDefault(rec.getString("passenger_nodejs"),
httpDomainSetupValidator.getProperty("passenger-nodejs").defaultValue() )),
entry("passenger-python", withDefault(rec.getString("passenger_python"),
httpDomainSetupValidator.getProperty("passenger-python").defaultValue() )),
entry("passenger-ruby", withDefault(rec.getString("passenger_ruby"),
httpDomainSetupValidator.getProperty("passenger-ruby").defaultValue() ))
)) ))
.build(); .build();
hostingAssets.put(DOMAIN_HTTP_SETUP_OFFSET + domain_id, domainHttpSetupAsset); hostingAssets.put(DOMAIN_HTTP_SETUP_OFFSET + domain_id, domainHttpSetupAsset);
@ -1291,39 +1300,54 @@ public class ImportHostingAssets extends ImportOfficeData {
}); });
} }
private void importZonefiles(final String zonenfilesJson) { private String withDefault(final String givenValue, final Object defaultValue) {
if (defaultValue instanceof String defaultStringValue) {
return givenValue != null && !givenValue.isBlank() ? givenValue : defaultStringValue;
}
throw new RuntimeException("property default value expected to be of type string, but is of type " + defaultValue.getClass().getSimpleName());
}
private void importZonefiles(final String vmName, final String zonenfilesJson) {
if (zonenfilesJson == null || zonenfilesJson.isEmpty() || zonenfilesJson.isBlank()) { if (zonenfilesJson == null || zonenfilesJson.isEmpty() || zonenfilesJson.isBlank()) {
return; return;
} }
final var objectMapper = new ObjectMapper();
try { try {
//noinspection unchecked //noinspection unchecked
final Map<String, Map<String, Object>> zoneData = objectMapper.readValue(zonenfilesJson, Map.class); final Map<String, Map<String, Object>> zoneData = jsonMapper.readValue(zonenfilesJson, Map.class);
importZonenfile(zoneData); importZonenfile(vmName, zoneData);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException("cannot read zonefile JSON: '"+zonenfilesJson+"'", e); throw new RuntimeException("cannot read zonefile JSON: '"+zonenfilesJson+"'", e);
} }
} }
private void importZonenfile(final Map<String, Map<String, Object>> zoneDataForVM) { private void importZonenfile( final String vmName, final Map<String, Map<String, Object>> zoneDataForVM) {
zoneDataForVM.forEach((domainName, zoneData) -> { zoneDataForVM.forEach((domainName, zoneData) -> {
//noinspection unchecked
final var domainAsset = domainSetupsByName.get(domainName); final var domainAsset = domainSetupsByName.get(domainName);
if (domainAsset != null) { if (domainAsset != null) {
final var domainDnsSetupAsset = domainAsset.getSubHostingAssets().stream() final var domainDnsSetupAsset = domainAsset.getSubHostingAssets().stream()
.filter(subAsset -> subAsset.getType() == DOMAIN_DNS_SETUP) .filter(subAsset -> subAsset.getType() == DOMAIN_DNS_SETUP)
.findAny().orElse(null); .findAny().orElse(null);
logError(() -> { assertThat(domainDnsSetupAsset).as(domainAsset.getIdentifier() + " has no DOMAIN_DNS_SETUP").isNotNull();
assertThat(domainDnsSetupAsset).as(domainAsset.getIdentifier() + " has no DOMAIN_DNS_SETUP").isNotNull();
zoneData.remove("DOM_OWNER"); // FIXME: doublecheck final var domUser = domainAsset.getSubHostingAssets().stream()
.filter(ha -> ha.getType() == DOMAIN_HTTP_SETUP)
.findAny().orElseThrow()
.getAssignedToAsset();
final var domOwner = zoneData.remove("DOM_OWNER");
final var expectedDomOwner = domUser.getIdentifier();
if ( domOwner.equals(expectedDomOwner) ) {
logError( () -> assertThat(vmName).isEqualTo(domUser.getParentAsset().getParentAsset().getIdentifier() ));
//noinspection unchecked //noinspection unchecked
zoneData.put("user-RR", ((ArrayList<ArrayList<Object>>) zoneData.get("user-RR")).stream() zoneData.put("user-RR", ((ArrayList<ArrayList<Object>>) zoneData.get("user-RR")).stream()
.map(userRR -> userRR.stream().map(Object::toString).collect(Collectors.joining(" "))) .map(userRR -> userRR.stream().map(Object::toString).collect(Collectors.joining(" ")))
.toArray(String[]::new) .toArray(String[]::new)
); );
domainDnsSetupAsset.getConfig().putAll(zoneData); domainDnsSetupAsset.getConfig().putAll(zoneData);
}); } else {
logError("zonedata dom_owner of " + domainAsset.getIdentifier() + " is " + domOwner + " but expected to be " + expectedDomOwner);
}
} }
}); });
} }

View File

@ -611,7 +611,7 @@ public class ImportOfficeData extends CsvDataImport {
@Order(9000) @Order(9000)
@ContinueOnFailure @ContinueOnFailure
void logCollectedErrorsBeforePersist() { void logCollectedErrorsBeforePersist() {
this.logErrors(); logErrors();
} }
@Test @Test

View File

@ -15,5 +15,5 @@ unixuser_id;name;comment;shell;homedir;locked;packet_id;userid;quota_softlimit;q
167846;hsh00-dph;hsh00-uph;/bin/false;/home/pacs/hsh00/users/uph;0;630;110568;0;0;0;0 167846;hsh00-dph;hsh00-uph;/bin/false;/home/pacs/hsh00/users/uph;0;630;110568;0;0;0;0
169546;dph00;Reinhard Wiese;/bin/bash;/home/pacs/dph00;0;19959;110593;0;0;0;0 169546;dph00;Reinhard Wiese;/bin/bash;/home/pacs/dph00;0;19959;110593;0;0;0;0
169596;dph00-uph;Domain admin;/bin/bash;/home/pacs/dph00/users/uph;0;19959;110594;0;0;0;0 169596;dph00-dph;Domain admin;/bin/bash;/home/pacs/dph00/users/uph;0;19959;110594;0;0;0;0

1 unixuser_id name comment shell homedir locked packet_id userid quota_softlimit quota_hardlimit storage_softlimit storage_hardlimit
15 169596 dph00-uph dph00-dph Domain admin /bin/bash /home/pacs/dph00/users/uph 0 19959 110594 0 0 0 0
16
17
18
19

View File

@ -1,6 +1,6 @@
{ {
"1981.ist-im-netz.de": { "1981.ist-im-netz.de": {
"DOM_OWNER": "mih00", "DOM_OWNER": "mim00",
"TTL": 21600, "TTL": 21600,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": false, "auto-AAAA-RR": false,
@ -19,7 +19,7 @@
"user-RR": [] "user-RR": []
}, },
"mellis.de": { "mellis.de": {
"DOM_OWNER": "mih00", "DOM_OWNER": "mim00",
"TTL": 21600, "TTL": 21600,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": true, "auto-AAAA-RR": true,
@ -67,7 +67,7 @@
] ]
}, },
"ist-im-netz.de": { "ist-im-netz.de": {
"DOM_OWNER": "mih00", "DOM_OWNER": "mim00",
"TTL": 14400, "TTL": 14400,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": false, "auto-AAAA-RR": false,
@ -146,26 +146,7 @@
"user-RR": [] "user-RR": []
}, },
"linuxfanboysngirls.de": { "linuxfanboysngirls.de": {
"DOM_OWNER": "lug00-marl", "DOM_OWNER": "lug00-wla.2",
"TTL": 21600,
"auto-A-RR": true,
"auto-AAAA-RR": false,
"auto-AUTOCONFIG-RR": false,
"auto-AUTODISCOVER-RR": false,
"auto-DKIM-RR": false,
"auto-MAILSERVICES-RR": false,
"auto-MX-RR": true,
"auto-NS-RR": true,
"auto-SOA": true,
"auto-SPF-RR": false,
"auto-WILDCARD-A-RR": true,
"auto-WILDCARD-AAAA-RR": false,
"auto-WILDCARD-MX-RR": true,
"auto-WILDCARD-SPF-RR": false,
"user-RR": []
},
"lug-in.de": {
"DOM_OWNER": "lug00-in",
"TTL": 21600, "TTL": 21600,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": false, "auto-AAAA-RR": false,
@ -184,7 +165,7 @@
"user-RR": [] "user-RR": []
}, },
"lug-mars.de": { "lug-mars.de": {
"DOM_OWNER": "lug00-marl", "DOM_OWNER": "lug00-wla.2",
"TTL": 14400, "TTL": 14400,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": false, "auto-AAAA-RR": false,
@ -253,7 +234,7 @@
] ]
}, },
"waera.de": { "waera.de": {
"DOM_OWNER": "mih00", "DOM_OWNER": "mim00",
"TTL": 21600, "TTL": 21600,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": false, "auto-AAAA-RR": false,
@ -272,7 +253,7 @@
"user-RR": [] "user-RR": []
}, },
"xn--wra-qla.de": { "xn--wra-qla.de": {
"DOM_OWNER": "mih00", "DOM_OWNER": "mim00",
"TTL": 21600, "TTL": 21600,
"auto-A-RR": true, "auto-A-RR": true,
"auto-AAAA-RR": false, "auto-AAAA-RR": false,

View File

@ -32,5 +32,58 @@
"\"v=spf1 include:spf.hostsharing.net ?all\"" "\"v=spf1 include:spf.hostsharing.net ?all\""
] ]
] ]
},
"mellis.de": {
"DOM_OWNER": "old00",
"TTL": 21600,
"auto-A-RR": true,
"auto-AAAA-RR": true,
"auto-AUTOCONFIG-RR": true,
"auto-AUTODISCOVER-RR": true,
"auto-DKIM-RR": true,
"auto-MAILSERVICES-RR": true,
"auto-MX-RR": true,
"auto-NS-RR": true,
"auto-SOA": true,
"auto-SPF-RR": false,
"auto-WILDCARD-A-RR": true,
"auto-WILDCARD-AAAA-RR": true,
"auto-WILDCARD-MX-RR": true,
"auto-WILDCARD-SPF-RR": true,
"user-RR": [
[
"dump.mellis.de.",
21600,
"IN",
"CNAME",
"mih12.hostsharing.net."
],
[
"key1._domainkey.mellis.de.",
21600,
"IN",
"TXT",
"\"v=DKIM1; k=rsa; t=s; h=sha256; s=email; \" \"p=OldFake+sN5uMa/\" \"OldFake/OldFake+OldFake/W2IITXPbLd9Z/OldFake+OldFake/\" \"OldFake+OldFake+OldFake\""
]
]
},
"ist-im-netz.de": {
"DOM_OWNER": "mim00",
"TTL": 700,
"auto-A-RR": true,
"auto-AAAA-RR": false,
"auto-AUTOCONFIG-RR": false,
"auto-AUTODISCOVER-RR": false,
"auto-DKIM-RR": false,
"auto-MAILSERVICES-RR": false,
"auto-MX-RR": true,
"auto-NS-RR": true,
"auto-SOA": true,
"auto-SPF-RR": false,
"auto-WILDCARD-A-RR": true,
"auto-WILDCARD-AAAA-RR": false,
"auto-WILDCARD-MX-RR": false,
"auto-WILDCARD-SPF-RR": false,
"user-RR": []
} }
} }