import-hosting-domain-assets (#84)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #84
This commit is contained in:
Michael Hoennig 2024-08-08 10:40:34 +02:00
parent 085876c772
commit 5046e9a296
19 changed files with 1174 additions and 250 deletions

View File

@ -33,4 +33,38 @@
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
<configuration default="false" name="ImportOfficeData" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="env">
<map>
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
</map>
</option>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":importOfficeData" />
<option value="--tests" />
<option value="&quot;net.hostsharing.hsadminng.hs.office.migration.ImportOfficeData&quot;" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<EXTENSION ID="com.intellij.execution.ExternalSystemRunConfigurationJavaExtension">
<extension name="coverage" sample_coverage="false" />
</EXTENSION>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@ -15,15 +15,16 @@ import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanPro
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
// TODO.impl: make package private once we've migrated the legacy data
public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
// according to RFC 1035 (section 5) and RFC 1034
static final String RR_REGEX_NAME = "([a-z0-9\\.-]+|@)\\s+";
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 = "[^;].*";
static final String RR_COMMENT = "(;.*)*";
static final String RR_REGEX_NAME = "(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+";
static final String RR_REGEX_TTL = "(([1-9][0-9]*[mMhHdDwW]?)+[ \t]+)?";
static final String RR_REGEX_IN = "[iI][nN][ \t]+"; // record class IN for Internet
static final String RR_RECORD_TYPE = "[a-zA-Z]+[ \t]+";
static final String RR_RECORD_DATA = "(([^;]+)|(\".*\")|(\\(.*\\)))[ \t]*";
static final String RR_COMMENT = "(;.*)?";
static final String RR_REGEX_TTL_IN =
RR_REGEX_NAME + RR_REGEX_TTL + RR_REGEX_IN + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
@ -32,26 +33,27 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
public static final String IDENTIFIER_SUFFIX = "|DNS";
private static List<String> zoneFileErrors = null; // TODO.impl: remove once legacy data is migrated
HsDomainDnsSetupHostingAssetValidator() {
super(
DOMAIN_DNS_SETUP,
AlarmContact.isOptional(),
integerProperty("TTL").min(0).withDefault(21600),
booleanProperty("auto-SOA-RR").withDefault(true),
booleanProperty("auto-SOA").withDefault(true),
booleanProperty("auto-NS-RR").withDefault(true),
booleanProperty("auto-MX-RR").withDefault(true),
booleanProperty("auto-A-RR").withDefault(true),
booleanProperty("auto-AAAA-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-DKIM-RR").withDefault(true),
booleanProperty("auto-SPF-RR").withDefault(true),
booleanProperty("auto-WILDCARD-MX-RR").withDefault(true),
booleanProperty("auto-WILDCARD-A-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),
arrayOf(
stringProperty("user-RR").matchesRegEx(RR_REGEX_TTL_IN, RR_REGEX_IN_TTL).required()
@ -78,33 +80,105 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
// TODO.spec: define which checks should get raised to error level
final var namedCheckZone = new SystemProcess("named-checkzone", fqdn(assetEntity));
if (namedCheckZone.execute(toZonefileString(assetEntity)) != 0) {
// yes, named-checkzone writes error messages to stdout
final var zonefileString = toZonefileString(assetEntity);
final var zoneFileErrorResult = zoneFileErrors != null ? zoneFileErrors : result;
if (namedCheckZone.execute(zonefileString) != 0) {
// yes, named-checkzone writes error messages to stdout, not stderr
stream(namedCheckZone.getStdOut().split("\n"))
.map(line -> line.replaceAll(" stream-0x[0-9a-f:]+", ""))
.forEach(result::add);
.map(line -> line.replaceAll(" stream-0x[0-9a-f]+:", "line "))
.map(line -> "[" + assetEntity.getIdentifier() + "] " + line)
.forEach(zoneFileErrorResult::add);
if (!namedCheckZone.getStdErr().isEmpty()) {
result.add("unexpected stderr output for " + namedCheckZone.getCommand() + ": " + namedCheckZone.getStdErr());
}
}
return result;
}
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 """
$ORIGIN {domain}.
$TTL {ttl}
; these records are just placeholders to create a valid zonefile for the validation
@ 1814400 IN SOA {domain}. root.{domain} ( 1999010100 10800 900 604800 86400 )
@ IN NS ns
{auto-SOA}
{auto-NS-RR}
{auto-MX-RR}
{auto-A-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}
"""
.replace("{ttl}", assetEntity.getDirectValue("TTL", Integer.class, 43200).toString())
.replace("{auto-SOA}", assetEntity.getDirectValue("auto-SOA", Boolean.class, false).equals(true)
? """
{domain}. 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
)
"""
: "; no auto-SOA"
)
.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 dns3.hostsharing.net.
"""
: "; 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("{ttl}", getPropertyValue(assetEntity, "TTL"))
.replace("{userRRs}", getPropertyValues(assetEntity, "user-RR"));
}
private String fqdn(final HsHostingAsset assetEntity) {
return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length() - IDENTIFIER_SUFFIX.length());
}
public static void addZonefileErrorsTo(final List<String> zoneFileErrors) {
HsDomainDnsSetupHostingAssetValidator.zoneFileErrors = zoneFileErrors;
}
}

View File

@ -13,8 +13,8 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsDomainHttpSetupHostingAssetValidator extends HostingAssetEntityValidator {
public static final String IDENTIFIER_SUFFIX = "|HTTP";
public static final String FILESYSTEM_PATH = "^/";
public static final String PARTIAL_DOMAIN_NAME_REGEX = "(?!-)[A-Za-z0-9-]{1,63}(?<!-)";
public static final String FILESYSTEM_PATH = "^/.*";
public static final String SUBDOMAIN_NAME_REGEX = "(\\*|(?!-)[A-Za-z0-9-]{1,63}(?<!-))";
HsDomainHttpSetupHostingAssetValidator() {
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-ruby").matchesRegEx(FILESYSTEM_PATH).provided("/usr/bin/ruby").withDefault("/usr/bin/ruby"),
arrayOf(
stringProperty("subdomains").matchesRegEx(PARTIAL_DOMAIN_NAME_REGEX).required()
stringProperty("subdomains").matchesRegEx(SUBDOMAIN_NAME_REGEX).required()
).optional());
}

View File

@ -9,7 +9,7 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMA
class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}";
public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}";
private final Pattern identifierPattern;

View File

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

View File

@ -1,22 +1,27 @@
package net.hostsharing.hsadminng.mapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.SneakyThrows;
import org.apache.commons.lang3.tuple.ImmutablePair;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining;
/** This class wraps another (usually persistent) map and
* supports applying `PatchMap` as well as a toString method with stable entry order.
*/
public class PatchableMapWrapper<T> implements Map<String, T> {
private static final ObjectMapper jsonWriter = new ObjectMapper()
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
.configure(SerializationFeature.INDENT_OUTPUT, true);
private final Map<String, T> delegate;
private PatchableMapWrapper(final Map<String, T> map) {
@ -53,24 +58,9 @@ public class PatchableMapWrapper<T> implements Map<String, T> {
});
}
@SneakyThrows
public String toString() {
return "{\n"
+ (
keySet().stream().sorted()
.map(k -> " \"" + k + "\": " + formatted(get(k))))
.collect(joining(",\n")
)
+ "\n}\n";
}
private Object formatted(final Object value) {
if ( value == null || value instanceof Number || value instanceof Boolean ) {
return value;
}
if ( value.getClass().isArray() ) {
return "\"" + Arrays.toString( (Object[]) value) + "\"";
}
return "\"" + value + "\"";
return jsonWriter.writeValueAsString(delegate);
}
// --- below just delegating methods --------------------------------

View File

@ -21,6 +21,11 @@ public class SystemProcess {
this.processBuilder = new ProcessBuilder(command);
}
public String getCommand() {
return processBuilder.command().toString();
}
public int execute() throws IOException, InterruptedException {
final var process = processBuilder.start();
stdOut = fetchOutput(process.getInputStream()); // yeah, twisted ProcessBuilder API

View File

@ -35,12 +35,27 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
.assignedToAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
.identifier("example.org|DNS")
.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(
"@ 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")
"ns IN A 192.0.2.2; IPv4 address for ns.example.com",
"_acme-challenge.PAULCHEN-VS.core.example.org. 60 IN CNAME _acme-challenge.core.example.org.acme-pki.de.")
)
));
}
@ -53,7 +68,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// then
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=integer, propertyName=TTL, min=0, defaultValue=21600}",
"{type=boolean, propertyName=auto-SOA-RR, defaultValue=true}",
"{type=boolean, propertyName=auto-SOA, defaultValue=true}",
"{type=boolean, propertyName=auto-NS-RR, defaultValue=true}",
"{type=boolean, propertyName=auto-MX-RR, defaultValue=true}",
"{type=boolean, propertyName=auto-A-RR, defaultValue=true}",
@ -66,9 +81,8 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
"{type=boolean, propertyName=auto-WILDCARD-MX-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-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+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}}"
);
}
@ -135,7 +149,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
}
@Test
void acceptsValidEntity() {
void acceptsValidEntityItself() {
// given
final var givenEntity = validEntityBuilder().build();
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
@ -147,6 +161,19 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
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
void rejectsInvalidProperties() {
// given
@ -166,35 +193,56 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// then
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.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-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 '@ 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 'www BAD1 Record-Class missing / not enough columns' does not match any");
}
@Test
void validStringMatchesRegEx() {
void validNameMatchesRegEx() {
assertThat("@ ").matches(RR_REGEX_NAME);
assertThat("ns ").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\t\t ").matches(RR_REGEX_TTL);
assertThat("12400 \t\t").matches(RR_REGEX_TTL);
assertThat("1h30m ").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\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\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("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; including a semicolon\"").matches(RR_RECORD_DATA);
}
@Test
void validCommentMatchesRegEx() {
assertThat("; whatever ; \" really anything").matches(RR_COMMENT);
}
@ -209,18 +257,42 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// then
assertThat(zonefile).isEqualTo("""
$ORIGIN example.org.
$TTL 21600
; these records are just placeholders to create a valid zonefile for the validation
@ 1814400 IN SOA example.org. root.example.org ( 1999010100 10800 900 604800 86400 )
@ IN NS ns
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 dns2.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 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"
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"
@ 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
_acme-challenge.PAULCHEN-VS.core.example.org. 60 IN CNAME _acme-challenge.core.example.org.acme-pki.de.
""");
}
@ -229,7 +301,8 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// 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)"
"example.org. 1814400 IN SOA example.org. root.example.org (1234 10800 900 604800 86400)",
"example.org. 1814400 IN SOA example.org. root.example.org (4321 10800 900 604800 86400)"
))
))
.build();
@ -240,9 +313,9 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// 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."
"[example.org|DNS] dns_master_load:line 26: example.org: multiple RRs of singleton type",
"[example.org|DNS] zone example.org/IN: loading from master file (null) failed: multiple RRs of singleton type",
"[example.org|DNS] zone example.org/IN: not loaded due to errors."
);
}
}

View File

@ -31,6 +31,7 @@ class HsDomainHttpSetupHostingAssetValidatorUnitTest {
.identifier("example.org|HTTP")
.config(Map.ofEntries(
entry("passenger-errorpage", true),
entry("fcgi-php-bin", "/usr/bin/whatsoever"),
entry("subdomains", Array.of("www", "test")
)
));
@ -54,10 +55,10 @@ class HsDomainHttpSetupHostingAssetValidatorUnitTest {
"{type=boolean, propertyName=includes, defaultValue=true}",
"{type=boolean, propertyName=letsencrypt, defaultValue=true}",
"{type=boolean, propertyName=multiviews, defaultValue=true}",
"{type=string, propertyName=fcgi-php-bin, matchesRegEx=[^/], provided=[/usr/lib/cgi-bin/php], defaultValue=/usr/lib/cgi-bin/php}",
"{type=string, propertyName=passenger-nodejs, matchesRegEx=[^/], provided=[/usr/bin/node], defaultValue=/usr/bin/node}",
"{type=string, propertyName=passenger-python, matchesRegEx=[^/], provided=[/usr/bin/python3], defaultValue=/usr/bin/python3}",
"{type=string, propertyName=passenger-ruby, matchesRegEx=[^/], provided=[/usr/bin/ruby], defaultValue=/usr/bin/ruby}",
"{type=string, propertyName=fcgi-php-bin, matchesRegEx=[^/.*], provided=[/usr/lib/cgi-bin/php], defaultValue=/usr/lib/cgi-bin/php}",
"{type=string, propertyName=passenger-nodejs, matchesRegEx=[^/.*], provided=[/usr/bin/node], defaultValue=/usr/bin/node}",
"{type=string, propertyName=passenger-python, matchesRegEx=[^/.*], provided=[/usr/bin/python3], defaultValue=/usr/bin/python3}",
"{type=string, propertyName=passenger-ruby, matchesRegEx=[^/.*], provided=[/usr/bin/ruby], defaultValue=/usr/bin/ruby}",
"{type=string[], propertyName=subdomains, elementsOf={type=string, propertyName=subdomains, matchesRegEx=[(?!-)[A-Za-z0-9-]{1,63}(?<!-)], required=true}}"
);
}
@ -155,7 +156,7 @@ class HsDomainHttpSetupHostingAssetValidatorUnitTest {
// then
assertThat(result).containsExactlyInAnyOrder(
"'DOMAIN_HTTP_SETUP:example.org|HTTP.config.htdocsfallback' is expected to be of type Boolean, but is of type String",
"'DOMAIN_HTTP_SETUP:example.org|HTTP.config.fcgi-php-bin' is expected to match any of [^/] but 'false' does not match",
"'DOMAIN_HTTP_SETUP:example.org|HTTP.config.fcgi-php-bin' is expected to match any of [^/.*] but 'false' does not match",
"'DOMAIN_HTTP_SETUP:example.org|HTTP.config.subdomains' is expected to match any of [(?!-)[A-Za-z0-9-]{1,63}(?<!-)] but '' does not match",
"'DOMAIN_HTTP_SETUP:example.org|HTTP.config.subdomains' is expected to match any of [(?!-)[A-Za-z0-9-]{1,63}(?<!-)] but '@' does not match",
"'DOMAIN_HTTP_SETUP:example.org|HTTP.config.subdomains' is expected to match any of [(?!-)[A-Za-z0-9-]{1,63}(?<!-)] but 'example.com' does not match");

View File

@ -49,7 +49,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
// then
assertThat(result).containsExactly(
"'identifier' expected to match '^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}', but is '"+testCase.domainName+"'"
"'identifier' expected to match '^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}', but is '"+testCase.domainName+"'"
);
}

View File

@ -80,6 +80,27 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
"'EMAIL_ALIAS:xyz00-office.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$, ^:include:/.*$, ^\\|.*$, ^/dev/null$] but 'garbage' does not match any");
}
@Test
void rejectsEmptyTargetArray() {
// given
final var emailAliasHostingAssetEntity = HsHostingAssetEntity.builder()
.type(EMAIL_ALIAS)
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
.identifier("xyz00-office")
.config(Map.ofEntries(
entry("target", new String[0])
))
.build();
final var validator = HostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(emailAliasHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ALIAS:xyz00-office.config.target' length is expected to be at min 1 but length of [[]] is 0");
}
@Test
void rejectsInvalidIndentifier() {
// given

View File

@ -11,6 +11,7 @@ import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestWatcher;
import org.opentest4j.AssertionFailedError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.MockBean;
@ -20,9 +21,9 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotNull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
@ -30,9 +31,9 @@ import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@ -40,7 +41,6 @@ import java.util.UUID;
import java.util.stream.Collectors;
import static java.lang.Boolean.parseBoolean;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
@ -77,7 +77,7 @@ public class CsvDataImport extends ContextBasedTest {
@MockBean
HttpServletRequest request;
static final List<String> errors = new ArrayList<>();
static final LinkedHashSet<String> errors = new LinkedHashSet<>();
public List<String[]> readAllLines(Reader reader) throws Exception {
@ -115,7 +115,20 @@ public class CsvDataImport extends ContextBasedTest {
}
protected Reader resourceReader(@NotNull final String resourcePath) {
try {
return new InputStreamReader(requireNonNull(getClass().getClassLoader().getResourceAsStream(resourcePath)));
} catch (Exception exc) {
throw new AssertionFailedError("cannot open '" + resourcePath + "'");
}
}
protected String resourceAsString(@NotNull final String resourcePath) {
try (InputStream inputStream = requireNonNull(getClass().getClassLoader().getResourceAsStream(resourcePath));
final var reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
} catch (Exception exc) {
throw new AssertionFailedError("cannot open '" + resourcePath + "'");
}
}
protected List<String[]> withoutHeader(final List<String[]> records) {
@ -127,7 +140,9 @@ public class CsvDataImport extends ContextBasedTest {
try (final var reader = new CSVReader(new StringReader(csvLine))) {
return stream(ofNullable(reader.readNext()).orElse(emptyArray(String.class)))
.map(String::trim)
.map(target -> target.startsWith("'") && target.endsWith("'") ? target.substring(1, target.length()-1) : target)
.map(target -> target.startsWith("'") && target.endsWith("'") ?
target.substring(1, target.length() - 1) :
target)
.toArray(String[]::new);
}
}
@ -198,11 +213,13 @@ public class CsvDataImport extends ContextBasedTest {
.setParameter("type", entity.getType().name())
.setParameter("bookingitemuuid", ofNullable(entity.getBookingItem()).map(BaseEntity::getUuid).orElse(null))
.setParameter("parentassetuuid", ofNullable(entity.getParentAsset()).map(BaseEntity::getUuid).orElse(null))
.setParameter("assignedtoassetuuid", ofNullable(entity.getAssignedToAsset()).map(BaseEntity::getUuid).orElse(null))
.setParameter(
"assignedtoassetuuid",
ofNullable(entity.getAssignedToAsset()).map(BaseEntity::getUuid).orElse(null))
.setParameter("alarmcontactuuid", ofNullable(entity.getAlarmContact()).map(BaseEntity::getUuid).orElse(null))
.setParameter("identifier", entity.getIdentifier())
.setParameter("caption", entity.getCaption())
.setParameter("config", entity.getConfig().toString())
.setParameter("config", entity.getConfig().toString().replace("\t", "\\t"))
.setParameter("version", entity.getVersion());
final var count = query.executeUpdate();
@ -212,17 +229,18 @@ public class CsvDataImport extends ContextBasedTest {
return entity;
}
protected <E> String toFormattedString(final Map<Integer, E> map) {
protected <E> String toJsonFormattedString(final Map<Integer, E> map) {
if ( map.isEmpty() ) {
return "{}";
}
return "{\n" +
final var json = "{\n" +
map.keySet().stream()
.map(id -> " " + id + "=" + map.get(id).toString())
.map(e -> e.replaceAll("\n ", " ").replace("\n", ""))
.map(e -> e.replaceAll("\n ", " ").replace("\n", "").replace(" : ", ": ").replace("{ ", "{").replace(", ", ", "))
.sorted()
.collect(Collectors.joining(",\n")) +
"\n}\n";
return json;
}
protected void deleteTestDataFromHsOfficeTables() {
@ -292,44 +310,26 @@ public class CsvDataImport extends ContextBasedTest {
try {
assertion.run();
} catch (final AssertionError exc) {
errors.add(exc.toString());
logError(exc.getMessage());
}
}
void logErrors() {
final var errorsToLog = new ArrayList<>(errors);
public static void logError(final String error) {
errors.add(error);
}
protected static void expectError(final String expectedError) {
final var found = errors.remove(expectedError);
if (!found) {
logError("expected but not found: " + expectedError);
}
}
protected final void assertNoErrors() {
final var errorsToLog = new LinkedHashSet<>(errors);
errors.clear();
assertThat(errorsToLog).isEmpty();
}
void expectErrors(final String... expectedErrors) {
assertContainsExactlyInAnyOrderIgnoringWhitespace(errors, expectedErrors);
}
private static class IgnoringWhitespaceComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
return s1.replaceAll("\\s", "").compareTo(s2.replaceAll("\\s", ""));
}
}
public static void assertContainsExactlyInAnyOrderIgnoringWhitespace(final List<String> expected, final List<String> actual) {
final var sortedExpected = expected.stream()
.map(m -> m.replaceAll("\\s+", " "))
.map(m -> m.replaceAll("^ ", ""))
.map(m -> m.replaceAll(" $", ""))
.toList();
final var sortedActual = actual.stream()
.map(m -> m.replaceAll("\\s+", " "))
.map(m -> m.replaceAll("^ ", ""))
.map(m -> m.replaceAll(" $", ""))
.toArray(String[]::new);
assertThat(sortedExpected).containsExactlyInAnyOrder(sortedActual);
}
public static void assertContainsExactlyInAnyOrderIgnoringWhitespace(final List<String> expected, final String... actual) {
assertContainsExactlyInAnyOrderIgnoringWhitespace(expected, asList(actual));
}
}
class Columns {
@ -408,7 +408,9 @@ class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback {
@Override
public void testFailed(final ExtensionContext context, final Throwable cause) {
previousTestsPassed = previousTestsPassed && context.getElement().map(e -> e.isAnnotationPresent(ContinueOnFailure.class)).orElse(false);
previousTestsPassed = previousTestsPassed && context.getElement()
.map(e -> e.isAnnotationPresent(ContinueOnFailure.class))
.orElse(false);
}
@Override

View File

@ -1,5 +1,7 @@
package net.hostsharing.hsadminng.hs.migration;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hash.HashGenerator;
import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm;
@ -10,6 +12,8 @@ import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityV
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
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.HostingAssetEntityValidatorRegistry;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.MethodOrderer;
@ -18,12 +22,15 @@ import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.Commit;
import org.springframework.test.annotation.DirtiesContext;
import java.io.Reader;
import java.net.IDN;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -32,6 +39,8 @@ import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.Arrays.stream;
import static java.util.Map.entry;
@ -40,6 +49,11 @@ import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
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_HTTP_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_MBOX_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.EMAIL_ALIAS;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
@ -112,6 +126,12 @@ public class ImportHostingAssets extends ImportOfficeData {
static final Integer DBINSTANCE_ID_OFFSET = 6000000;
static final Integer DBUSER_ID_OFFSET = 7000000;
static final Integer DB_ID_OFFSET = 8000000;
static final Integer DOMAIN_SETUP_OFFSET = 10000000;
static final Integer DOMAIN_DNS_SETUP_OFFSET = 11000000;
static final Integer DOMAIN_HTTP_SETUP_OFFSET = 12000000;
static final Integer DOMAIN_MBOX_SETUP_OFFSET = 13000000;
static final Integer DOMAIN_SMTP_SETUP_OFFSET = 14000000;
static List<String> zonefileErrors = new ArrayList<>();
record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference<HsHostingAssetRealEntity> serverRef) {}
@ -120,6 +140,9 @@ public class ImportHostingAssets extends ImportOfficeData {
static Map<Integer, Hive> hives = new WriteOnceMap<>();
static Map<Integer, HsHostingAssetRealEntity> hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type?
static Map<String, HsHostingAssetRealEntity> dbUsersByEngineAndName = new WriteOnceMap<>();
static Map<String, HsHostingAssetRealEntity> domainSetupsByName = new WriteOnceMap<>();
final ObjectMapper jsonMapper = new ObjectMapper();
@Test
@Order(11010)
@ -175,7 +198,7 @@ public class ImportHostingAssets extends ImportOfficeData {
void verifyHives() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(first(5, hives))).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(first(5, hives))).isEqualToIgnoringWhitespace("""
{
2000001=Hive[hive_id=1, hive_name=h00, inet_addr_id=358, serverRef=null],
2000002=Hive[hive_id=2, hive_name=h01, inet_addr_id=359, serverRef=null],
@ -311,7 +334,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}),
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}),
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})
}
""");
}
@ -334,17 +357,15 @@ public class ImportHostingAssets extends ImportOfficeData {
assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace("""
{
5002403=HsHostingAssetRealEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}),
5002405=HsHostingAssetRealEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}),
5002429=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}),
5002431=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}),
5002449=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}),
5002451=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}),
5002452=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { "target": "[]"}),
5002453=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]"}),
5002454=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { "target": "[/dev/null]"}),
5002455=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"}),
5002456=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-1_with_single_quotes, mim00-1_with_single_quotes, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/rir00/mailinglist/ecartis -r kybs06-intern]"})
5002403=HsHostingAssetRealEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, {"target": [ "michael.mellis@example.com" ]}),
5002405=HsHostingAssetRealEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, {"target": [ "|/home/pacs/lug00/users/in/mailinglist/listar" ]}),
5002429=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, {"target": [ "mim12-mi@mim12.hostsharing.net" ]}),
5002431=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, {"target": [ "michael.mellis@hostsharing.net" ]}),
5002449=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, {"target": [ "mim00-hhfx", "|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l" ]}),
5002451=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, {"target": [ ":include:/home/pacs/mim00/etc/hhfx.list" ]}),
5002454=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, {"target": [ "/dev/null" ]}),
5002455=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, {"target": [ "|/home/pacs/mim00/install/corpslistar/listar" ]}),
5002456=HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-1_with_single_quotes, mim00-1_with_single_quotes, MANAGED_WEBSPACE:mim00, {"target": [ "|/home/pacs/rir00/mailinglist/ecartis -r kybs06-intern" ]})
}
""");
}
@ -438,6 +459,94 @@ public class ImportHostingAssets extends ImportOfficeData {
""");
}
@Test
@Order(16010)
void importDomains() {
try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/domain.csv")) {
final var lines = readAllLines(reader);
importDomains(justHeader(lines), withoutHeader(lines));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Test
@Order(16020)
void importZonenfiles() {
final var reflections = new Reflections(MIGRATION_DATA_PATH + "/hosting/zonefiles", new ResourcesScanner());
final var zonefileFiles = reflections.getResources(Pattern.compile(".*\\.json")).stream().sorted().toList();
zonefileFiles.forEach(zonenfileName -> {
System.out.println("Processing zonenfile: " + zonenfileName);
importZonefiles(vmName(zonenfileName), resourceAsString(zonenfileName));
});
}
private String vmName(final String zonenfileName) {
return zonenfileName.substring(zonenfileName.length() - "vm0000.json".length()).substring(0, 6);
}
@Test
@Order(16019)
void verifyDomains() {
assumeThatWeAreImportingControlledTestData();
assertThat(firstOfEachType(
12,
DOMAIN_SETUP,
DOMAIN_DNS_SETUP,
DOMAIN_HTTP_SETUP,
DOMAIN_MBOX_SETUP,
DOMAIN_SMTP_SETUP)).isEqualToIgnoringWhitespace("""
{
10004531=HsHostingAssetRealEntity(DOMAIN_SETUP, l-u-g.org, l-u-g.org),
10004532=HsHostingAssetRealEntity(DOMAIN_SETUP, linuxfanboysngirls.de, linuxfanboysngirls.de),
10004534=HsHostingAssetRealEntity(DOMAIN_SETUP, lug-mars.de, lug-mars.de),
10004581=HsHostingAssetRealEntity(DOMAIN_SETUP, 1981.ist-im-netz.de, 1981.ist-im-netz.de, DOMAIN_SETUP:ist-im-netz.de),
10004587=HsHostingAssetRealEntity(DOMAIN_SETUP, mellis.de, mellis.de),
10004589=HsHostingAssetRealEntity(DOMAIN_SETUP, ist-im-netz.de, ist-im-netz.de),
10004600=HsHostingAssetRealEntity(DOMAIN_SETUP, waera.de, waera.de),
10004604=HsHostingAssetRealEntity(DOMAIN_SETUP, xn--wra-qla.de, wära.de),
10027662=HsHostingAssetRealEntity(DOMAIN_SETUP, dph-netzwerk.de, dph-netzwerk.de),
11004531=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, l-u-g.org|DNS, DNS-Setup für l-u-g.org, DOMAIN_SETUP:l-u-g.org, MANAGED_WEBSPACE:lug00),
11004532=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, linuxfanboysngirls.de|DNS, DNS-Setup für linuxfanboysngirls.de, DOMAIN_SETUP:linuxfanboysngirls.de, MANAGED_WEBSPACE:lug00),
11004534=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, lug-mars.de|DNS, DNS-Setup für lug-mars.de, DOMAIN_SETUP:lug-mars.de, MANAGED_WEBSPACE:lug00),
11004581=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, 1981.ist-im-netz.de|DNS, DNS-Setup für 1981.ist-im-netz.de, DOMAIN_SETUP:1981.ist-im-netz.de, MANAGED_WEBSPACE:mim00),
11004587=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, mellis.de|DNS, DNS-Setup für mellis.de, DOMAIN_SETUP:mellis.de, MANAGED_WEBSPACE:mim00),
11004589=HsHostingAssetRealEntity(DOMAIN_DNS_SETUP, ist-im-netz.de|DNS, DNS-Setup für ist-im-netz.de, DOMAIN_SETUP:ist-im-netz.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),
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", "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", "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", "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", "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", "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", "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", "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", "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-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),
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),
13004581=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, 1981.ist-im-netz.de|MBOX, E-Mail-Empfang-Setup für 1981.ist-im-netz.de, DOMAIN_SETUP:1981.ist-im-netz.de, MANAGED_WEBSPACE:mim00),
13004587=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, mellis.de|MBOX, E-Mail-Empfang-Setup für mellis.de, DOMAIN_SETUP:mellis.de, MANAGED_WEBSPACE:mim00),
13004589=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, ist-im-netz.de|MBOX, E-Mail-Empfang-Setup für ist-im-netz.de, DOMAIN_SETUP:ist-im-netz.de, MANAGED_WEBSPACE:mim00),
13004600=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, waera.de|MBOX, E-Mail-Empfang-Setup für waera.de, DOMAIN_SETUP:waera.de, MANAGED_WEBSPACE:mim00),
13004604=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, xn--wra-qla.de|MBOX, E-Mail-Empfang-Setup für wära.de, DOMAIN_SETUP:xn--wra-qla.de, MANAGED_WEBSPACE:mim00),
13027662=HsHostingAssetRealEntity(DOMAIN_MBOX_SETUP, dph-netzwerk.de|MBOX, E-Mail-Empfang-Setup für dph-netzwerk.de, DOMAIN_SETUP:dph-netzwerk.de, MANAGED_WEBSPACE:dph00),
14004531=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, l-u-g.org|SMTP, E-Mail-Versand-Setup für l-u-g.org, DOMAIN_SETUP:l-u-g.org, MANAGED_WEBSPACE:lug00),
14004532=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, linuxfanboysngirls.de|SMTP, E-Mail-Versand-Setup für linuxfanboysngirls.de, DOMAIN_SETUP:linuxfanboysngirls.de, MANAGED_WEBSPACE:lug00),
14004534=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, lug-mars.de|SMTP, E-Mail-Versand-Setup für lug-mars.de, DOMAIN_SETUP:lug-mars.de, MANAGED_WEBSPACE:lug00),
14004581=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, 1981.ist-im-netz.de|SMTP, E-Mail-Versand-Setup für 1981.ist-im-netz.de, DOMAIN_SETUP:1981.ist-im-netz.de, MANAGED_WEBSPACE:mim00),
14004587=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, mellis.de|SMTP, E-Mail-Versand-Setup für mellis.de, DOMAIN_SETUP:mellis.de, MANAGED_WEBSPACE:mim00),
14004589=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, ist-im-netz.de|SMTP, E-Mail-Versand-Setup für ist-im-netz.de, DOMAIN_SETUP:ist-im-netz.de, MANAGED_WEBSPACE:mim00),
14004600=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, waera.de|SMTP, E-Mail-Versand-Setup für waera.de, DOMAIN_SETUP:waera.de, MANAGED_WEBSPACE:mim00),
14004604=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, xn--wra-qla.de|SMTP, E-Mail-Versand-Setup für wära.de, DOMAIN_SETUP:xn--wra-qla.de, MANAGED_WEBSPACE:mim00),
14027662=HsHostingAssetRealEntity(DOMAIN_SMTP_SETUP, dph-netzwerk.de|SMTP, E-Mail-Versand-Setup für dph-netzwerk.de, DOMAIN_SETUP:dph-netzwerk.de, MANAGED_WEBSPACE:dph00)
}
""");
}
// --------------------------------------------------------------------------------------------
@Test
@ -471,7 +580,11 @@ public class ImportHostingAssets extends ImportOfficeData {
@Order(18999)
@ContinueOnFailure
void logValidationErrors() {
this.logErrors();
if (isImportingControlledTestData()) {
expectError("zonedata dom_owner of mellis.de is old00 but expected to be mim00");
expectError("\nexpected: \"vm1068\"\n but was: \"vm1093\"");
}
this.assertNoErrors();
}
// --------------------------------------------------------------------------------------------
@ -576,6 +689,46 @@ public class ImportHostingAssets extends ImportOfficeData {
persistHostingAssetsOfType(PGSQL_DATABASE, MARIADB_DATABASE);
}
@Test
@Order(19300)
@Commit
void persistDomainSetups() {
System.out.println("PERSISTING domain setups to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'");
persistHostingAssetsOfType(DOMAIN_SETUP);
}
@Test
@Order(19301)
@Commit
void persistDomainDnsSetups() {
System.out.println("PERSISTING domain DNS setups to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'");
persistHostingAssetsOfType(DOMAIN_DNS_SETUP);
}
@Test
@Order(19302)
@Commit
void persistDomainHttpSetups() {
System.out.println("PERSISTING domain HTTP setups to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'");
persistHostingAssetsOfType(DOMAIN_HTTP_SETUP);
}
@Test
@Order(19303)
@Commit
void persistDomainMboxSetups() {
System.out.println("PERSISTING domain MBOX setups to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'");
persistHostingAssetsOfType(DOMAIN_MBOX_SETUP);
}
@Test
@Order(19304)
@Commit
void persistDomainSmtpSetups() {
System.out.println("PERSISTING domain SMTP setups to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'");
persistHostingAssetsOfType(DOMAIN_SMTP_SETUP);
}
@Test
@Order(19900)
void verifyPersistedUnixUsersWithUserId() {
@ -596,7 +749,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}),
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}),
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})
}
""");
}
@ -604,37 +757,26 @@ public class ImportHostingAssets extends ImportOfficeData {
@Test
@Order(19910)
void verifyBookingItemsAreActuallyPersisted() {
final var biCount = (Integer) em.createNativeQuery("SELECT count(*) FROM hs_booking_item", Integer.class).getSingleResult();
final var biCount = (Integer) em.createNativeQuery("select count(*) from hs_booking_item", Integer.class)
.getSingleResult();
assertThat(biCount).isGreaterThan(isImportingControlledTestData() ? 5 : 500);
}
@Test
@Order(19920)
void verifyHostingAssetsAreActuallyPersisted() {
final var haCount = (Integer) em.createNativeQuery("SELECT count(*) FROM hs_hosting_asset", Integer.class).getSingleResult();
assertThat(haCount).isGreaterThan(isImportingControlledTestData() ? 30 : 10000);
final var haCount = (Integer) em.createNativeQuery("select count(*) from hs_hosting_asset", Integer.class)
.getSingleResult();
assertThat(haCount).isGreaterThan(isImportingControlledTestData() ? 40 : 15000);
}
// ============================================================================================
@Test
@Order(99999)
void logErrors() {
if (isImportingControlledTestData()) {
super.expectErrors("""
validation failed for id:5002452( HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, {
"target": "[]"
}
)): ['EMAIL_ALIAS:mim00-empty.config.target' length is expected to be at min 1 but length of [[]] is 0]""",
"""
validation failed for id:5002453( HsHostingAssetRealEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, {
"target": "[]"
}
)): ['EMAIL_ALIAS:mim00-0_entries.config.target' length is expected to be at min 1 but length of [[]] is 0]"""
);
} else {
super.logErrors();
}
@Order(19999)
void logErrorsAfterPersistingHostingAssets() {
errors.addAll(zonefileErrors);
assertNoErrors();
}
private void persistRecursively(final Integer key, final HsBookingItemEntity bi) {
@ -648,20 +790,26 @@ public class ImportHostingAssets extends ImportOfficeData {
private void persistHostingAssetsOfType(final HsHostingAssetType... hsHostingAssetTypes) {
final var hsHostingAssetTypeSet = stream(hsHostingAssetTypes).collect(toSet());
jpaAttempt.transacted(() -> {
if (hsHostingAssetTypeSet.contains(DOMAIN_DNS_SETUP)) {
HsDomainDnsSetupHostingAssetValidator.addZonefileErrorsTo(zonefileErrors);
}
jpaAttempt.transacted(() ->
hostingAssets.forEach((key, ha) -> {
context(rbacSuperuser);
if (hsHostingAssetTypeSet.contains(ha.getType())) {
context(rbacSuperuser); // if put only outside the loop, it seems to get lost after a while, no idea why
logError(() ->
new HostingAssetEntitySaveProcessor(em, ha)
.preprocessEntity()
.validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*")
.prepareForSave()
.saveUsing(entity -> persist(key, entity))
.validateContext();
}
}
.validateContext()
);
}).assertSuccessful();
}
})
).assertSuccessful();
}
private void importIpNumbers(final String[] header, final List<String[]> records) {
@ -895,11 +1043,13 @@ public class ImportHostingAssets extends ImportOfficeData {
// TODO.spec: crop SSD+HDD limits if > booked
if (unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0)
> 1024 * unixUserAsset.getContextValue("SSD", Integer.class, 0)) {
unixUserAsset.getConfig().put("SSD hard quota", unixUserAsset.getContextValue("SSD", Integer.class, 0)*1024);
unixUserAsset.getConfig()
.put("SSD hard quota", unixUserAsset.getContextValue("SSD", Integer.class, 0) * 1024);
}
if (unixUserAsset.getDirectValue("HDD hard quota", Integer.class, 0)
> 1024 * unixUserAsset.getContextValue("HDD", Integer.class, 0)) {
unixUserAsset.getConfig().put("HDD hard quota", unixUserAsset.getContextValue("HDD", Integer.class, 0)*1024);
unixUserAsset.getConfig()
.put("HDD hard quota", unixUserAsset.getContextValue("HDD", Integer.class, 0) * 1024);
}
// TODO.spec: does `softlimit<hardlimit?` even make sense? Fix it in this or the other direction?
@ -986,7 +1136,9 @@ public class ImportHostingAssets extends ImportOfficeData {
: failWith("unknown DB engine " + engine);
final var hash = dbUserAssetType == MARIADB_USER ? Algorithm.MYSQL_NATIVE : Algorithm.SCRAM_SHA256;
final var name = rec.getString("name");
final var password_hash = rec.getString("password_hash", HashGenerator.using(hash).withRandomSalt().hash("fake pw " + name));
final var password_hash = rec.getString(
"password_hash",
HashGenerator.using(hash).withRandomSalt().hash("fake pw " + name));
final HsHostingAssetType dbInstanceAssetType = "mysql".equals(engine) ? MARIADB_INSTANCE
: "pgsql".equals(engine) ? PGSQL_INSTANCE
@ -1033,13 +1185,191 @@ public class ImportHostingAssets extends ImportOfficeData {
.identifier(type.name().substring(0, 2) + "D|" + name)
.caption(name)
.config(ofEntries(
entry("encoding", type == MARIADB_DATABASE ? encoding.toLowerCase() : encoding.toUpperCase())
entry(
"encoding",
type == MARIADB_DATABASE ? encoding.toLowerCase() : encoding.toUpperCase())
))
.build();
hostingAssets.put(DB_ID_OFFSET + database_id, dbAsset);
});
}
private void importDomains(final String[] header, final List<String[]> records) {
final var httpDomainSetupValidator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_HTTP_SETUP);
final var columns = new Columns(header);
records.stream()
.map(this::trimAll)
.map(row -> new Record(columns, row))
.forEach(rec -> {
final var domain_id = rec.getInteger("domain_id");
final var domain_name = rec.getString("domain_name");
// final var domain_since = rec.getString("domain_since");
// final var domain_dns_master = rec.getString("domain_dns_master");
final var owner_id = rec.getInteger("domain_owner");
final var domainoptions = rec.getString("domainoptions");
// Domain Setup
final var domainSetupAsset = HsHostingAssetRealEntity.builder()
.type(DOMAIN_SETUP)
// .parentAsset(parentDomainSetupAsset) are set once we've collected all of them
.identifier(domain_name)
.caption(IDN.toUnicode(domain_name))
.config(ofEntries(
// nothing here
))
.build();
domainSetupsByName.put(domain_name, domainSetupAsset);
hostingAssets.put(DOMAIN_SETUP_OFFSET + domain_id, domainSetupAsset);
domainSetupAsset.setSubHostingAssets(new ArrayList<>());
// Domain DNS Setup
final var ownerAsset = hostingAssets.get(UNIXUSER_ID_OFFSET + owner_id);
final var webspaceAsset = ownerAsset.getParentAsset();
assertThat(webspaceAsset.getType()).isEqualTo(MANAGED_WEBSPACE);
final var domainDnsSetupAsset = HsHostingAssetRealEntity.builder()
.type(DOMAIN_DNS_SETUP)
.parentAsset(domainSetupAsset)
.assignedToAsset(webspaceAsset)
.identifier(domain_name + "|DNS")
.caption("DNS-Setup für " + IDN.toUnicode(domain_name))
.config(new HashMap<>()) // is read from separate files
.build();
hostingAssets.put(DOMAIN_DNS_SETUP_OFFSET + domain_id, domainDnsSetupAsset);
domainSetupAsset.getSubHostingAssets().add(domainDnsSetupAsset);
// Domain HTTP Setup
final var options = stream(domainoptions.split(",")).collect(toSet());
final var domainHttpSetupAsset = HsHostingAssetRealEntity.builder()
.type(DOMAIN_HTTP_SETUP)
.parentAsset(domainSetupAsset)
.assignedToAsset(ownerAsset)
.identifier(domain_name + "|HTTP")
.caption("HTTP-Setup für " + IDN.toUnicode(domain_name))
.config(ofEntries(
entry("htdocsfallback", options.contains("htdocsfallback")),
entry("indexes", options.contains("indexes")),
entry("cgi", options.contains("cgi")),
entry("passenger", options.contains("passenger")),
entry("passenger-errorpage", options.contains("passenger-errorpage")),
entry("fastcgi", options.contains("fastcgi")),
entry("autoconfig", options.contains("autoconfig")),
entry("greylisting", options.contains("greylisting")),
entry("includes", options.contains("includes")),
entry("letsencrypt", options.contains("letsencrypt")),
entry("multiviews", options.contains("multiviews")),
entry("subdomains", withDefault(rec.getString("valid_subdomain_names"), "*")
.split(",")),
entry("fcgi-php-bin", withDefault(
rec.getString("fcgi_php_bin"),
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();
hostingAssets.put(DOMAIN_HTTP_SETUP_OFFSET + domain_id, domainHttpSetupAsset);
domainSetupAsset.getSubHostingAssets().add(domainHttpSetupAsset);
// Domain MBOX Setup
final var domainMboxSetupAsset = HsHostingAssetRealEntity.builder()
.type(DOMAIN_MBOX_SETUP)
.parentAsset(domainSetupAsset)
.assignedToAsset(webspaceAsset)
.identifier(domain_name + "|MBOX")
.caption("E-Mail-Empfang-Setup für " + IDN.toUnicode(domain_name))
.config(ofEntries(
// no properties available
))
.build();
hostingAssets.put(DOMAIN_MBOX_SETUP_OFFSET + domain_id, domainMboxSetupAsset);
domainSetupAsset.getSubHostingAssets().add(domainMboxSetupAsset);
// Domain SMTP Setup
final var domainSmtpSetupAsset = HsHostingAssetRealEntity.builder()
.type(DOMAIN_SMTP_SETUP)
.parentAsset(domainSetupAsset)
.assignedToAsset(webspaceAsset)
.identifier(domain_name + "|SMTP")
.caption("E-Mail-Versand-Setup für " + IDN.toUnicode(domain_name))
.config(ofEntries(
// no properties available
))
.build();
hostingAssets.put(DOMAIN_SMTP_SETUP_OFFSET + domain_id, domainSmtpSetupAsset);
domainSetupAsset.getSubHostingAssets().add(domainSmtpSetupAsset);
});
domainSetupsByName.values().forEach(domainSetup -> {
final var parentDomainName = domainSetup.getIdentifier().split("\\.", 2)[1];
final var parentDomainSetup = domainSetupsByName.get(parentDomainName);
if (parentDomainSetup != null) {
domainSetup.setParentAsset(parentDomainSetup);
}
});
}
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()) {
return;
}
try {
//noinspection unchecked
final Map<String, Map<String, Object>> zoneData = jsonMapper.readValue(zonenfilesJson, Map.class);
importZonenfile(vmName, zoneData);
} catch (JsonProcessingException e) {
throw new RuntimeException("cannot read zonefile JSON: '" + zonenfilesJson + "'", e);
}
}
private void importZonenfile(final String vmName, final Map<String, Map<String, Object>> zoneDataForVM) {
zoneDataForVM.forEach((domainName, zoneData) -> {
final var domainAsset = domainSetupsByName.get(domainName);
if (domainAsset != null) {
final var domainDnsSetupAsset = domainAsset.getSubHostingAssets().stream()
.filter(subAsset -> subAsset.getType() == DOMAIN_DNS_SETUP)
.findAny().orElse(null);
assertThat(domainDnsSetupAsset).as(domainAsset.getIdentifier() + " has no DOMAIN_DNS_SETUP").isNotNull();
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
zoneData.put("user-RR", ((ArrayList<ArrayList<Object>>) zoneData.get("user-RR")).stream()
.map(userRR -> userRR.stream().map(Object::toString).collect(Collectors.joining(" ")))
.toArray(String[]::new)
);
domainDnsSetupAsset.getConfig().putAll(zoneData);
} else {
logError("zonedata dom_owner of " + domainAsset.getIdentifier() + " is " + domOwner + " but expected to be "
+ expectedDomOwner);
}
}
});
}
// ============================================================================================
<V> V returning(
@ -1084,7 +1414,7 @@ public class ImportHostingAssets extends ImportOfficeData {
private String firstOfEachType(
final int maxCount,
final HsHostingAssetType... types) {
return toFormattedString(stream(types)
return toJsonFormattedString(stream(types)
.flatMap(t ->
hostingAssets.entrySet().stream()
.filter(hae -> hae.getValue().getType() == t)
@ -1100,7 +1430,7 @@ public class ImportHostingAssets extends ImportOfficeData {
private String firstOfEachType(
final int maxCount,
final HsBookingItemType... types) {
return toFormattedString(stream(types)
return toJsonFormattedString(stream(types)
.flatMap(t ->
bookingItems.entrySet().stream()
.filter(bie -> bie.getValue().getType() == t)

View File

@ -105,6 +105,7 @@ public class ImportOfficeData extends CsvDataImport {
// at least as the number of lines in business_partners.csv from test-data, but less than real data partner count
public static final int MAX_NUMBER_OF_TEST_DATA_PARTNERS = 100;
public static final int DELIBERATELY_BROKEN_BUSINESS_PARTNER_ID = 199;
static int relationId = 2000000;
@ -151,7 +152,7 @@ public class ImportOfficeData extends CsvDataImport {
assumeThatWeAreImportingControlledTestData();
// no contacts yet => mostly null values
assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(partners)).isEqualToIgnoringWhitespace("""
{
100=partner(P-10003: null null, null),
120=partner(P-10020: null null, null),
@ -164,8 +165,8 @@ public class ImportOfficeData extends CsvDataImport {
542=partner(P-11019: null null, null)
}
""");
assertThat(toFormattedString(contacts)).isEqualTo("{}");
assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(contacts)).isEqualTo("{}");
assertThat(toJsonFormattedString(debitors)).isEqualToIgnoringWhitespace("""
{
100=debitor(D-1000300: rel(anchor='null null, null', type='DEBITOR'), mim),
120=debitor(D-1002000: rel(anchor='null null, null', type='DEBITOR'), xyz),
@ -178,7 +179,7 @@ public class ImportOfficeData extends CsvDataImport {
542=debitor(D-1101900: rel(anchor='null null, null', type='DEBITOR'), dph)
}
""");
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{
100=Membership(M-1000300, P-10003, [2000-12-06,), ACTIVE),
120=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
@ -206,7 +207,7 @@ public class ImportOfficeData extends CsvDataImport {
void verifyContacts() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(partners)).isEqualToIgnoringWhitespace("""
{
100=partner(P-10003: ?? Michael Mellis, Herr Michael Mellis , Michael Mellis),
120=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH),
@ -219,7 +220,7 @@ public class ImportOfficeData extends CsvDataImport {
542=partner(P-11019: ?? Das Perfekte Haus, Herr Richard Wiese , Das Perfekte Haus)
}
""");
assertThat(toFormattedString(contacts)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(contacts)).isEqualToIgnoringWhitespace("""
{
100=contact(caption='Herr Michael Mellis , Michael Mellis', emailAddresses='{ "main": "michael@Mellis.example.org"}'),
1200=contact(caption='JM e.K.', emailAddresses='{ "main": "jm-ex-partner@example.org"}'),
@ -241,7 +242,7 @@ public class ImportOfficeData extends CsvDataImport {
90698=contact(caption='Jan Henning ', emailAddresses='{ "main": "mail@jan-henning.example.org"}')
}
""");
assertThat(toFormattedString(persons)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(persons)).isEqualToIgnoringWhitespace("""
{
100=person(personType='??', tradeName='Michael Mellis', familyName='Mellis', givenName='Michael'),
1200=person(personType='LP', tradeName='JM e.K.'),
@ -263,7 +264,7 @@ public class ImportOfficeData extends CsvDataImport {
90698=person(personType='NP', familyName='Henning', givenName='Jan')
}
""");
assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(debitors)).isEqualToIgnoringWhitespace("""
{
100=debitor(D-1000300: rel(anchor='?? Michael Mellis', type='DEBITOR', holder='?? Michael Mellis'), mim),
120=debitor(D-1002000: rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH'), xyz),
@ -276,7 +277,7 @@ public class ImportOfficeData extends CsvDataImport {
542=debitor(D-1101900: rel(anchor='?? Das Perfekte Haus', type='DEBITOR', holder='?? Das Perfekte Haus'), dph)
}
""");
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{
100=Membership(M-1000300, P-10003, [2000-12-06,), ACTIVE),
120=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
@ -286,7 +287,7 @@ public class ImportOfficeData extends CsvDataImport {
542=Membership(M-1101900, P-11019, [2021-05-25,), ACTIVE)
}
""");
assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(relations)).isEqualToIgnoringWhitespace("""
{
2000000=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
2000001=rel(anchor='?? Michael Mellis', type='DEBITOR', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
@ -373,7 +374,7 @@ public class ImportOfficeData extends CsvDataImport {
void verifySepaMandates() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(bankAccounts)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(bankAccounts)).isEqualToIgnoringWhitespace("""
{
132=bankAccount(DE37500105177419788228: holder='Michael Mellis', bic='GENODEF1HH2'),
234234=bankAccount(DE37500105177419788228: holder='Michael Mellis', bic='INGDDEFFXXX'),
@ -384,7 +385,7 @@ public class ImportOfficeData extends CsvDataImport {
387=bankAccount(DE89370400440532013000: holder='Richard Wiese Das Perfekte Haus', bic='COBADEFFXXX')
}
""");
assertThat(toFormattedString(sepaMandates)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(sepaMandates)).isEqualToIgnoringWhitespace("""
{
132=SEPA-Mandate(DE37500105177419788228, HS-10003-20140801, 2013-12-01, [2013-12-01,)),
234234=SEPA-Mandate(DE37500105177419788228, MH12345, 2004-06-12, [2004-06-15,)),
@ -413,7 +414,7 @@ public class ImportOfficeData extends CsvDataImport {
void verifyCoopShares() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(coopShares)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(coopShares)).isEqualToIgnoringWhitespace("""
{
241=CoopShareTransaction(M-1000300: 2011-12-05, SUBSCRIPTION, 16, 1000300),
279=CoopShareTransaction(M-1015200: 2013-10-21, SUBSCRIPTION, 1, 1015200),
@ -446,7 +447,7 @@ public class ImportOfficeData extends CsvDataImport {
void verifyCoopAssets() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(coopAssets)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(coopAssets)).isEqualToIgnoringWhitespace("""
{
1093=CoopAssetsTransaction(M-1000300: 2023-10-05, DEPOSIT, 3072, 1000300, Kapitalerhoehung - Ueberweisung),
1094=CoopAssetsTransaction(M-1000300: 2023-10-06, DEPOSIT, 3072, 1000300, Kapitalerhoehung - Ueberweisung),
@ -475,7 +476,7 @@ public class ImportOfficeData extends CsvDataImport {
void verifyMemberships() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
assertThat(toJsonFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{
100=Membership(M-1000300, P-10003, [2000-12-06,), ACTIVE),
120=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
@ -494,11 +495,15 @@ public class ImportOfficeData extends CsvDataImport {
partners.forEach((id, p) -> {
final var partnerRel = p.getPartnerRel();
assertThat(partnerRel).describedAs("partner " + id + " without partnerRel").isNotNull();
if ( id != 199 ) {
logError( () -> assertThat(partnerRel.getContact()).describedAs("partner " + id + " without partnerRel.contact").isNotNull());
logError( () -> assertThat(partnerRel.getContact().getCaption()).describedAs("partner " + id + " without valid partnerRel.contact").isNotNull());
logError( () -> assertThat(partnerRel.getHolder()).describedAs("partner " + id + " without partnerRel.relHolder").isNotNull());
logError( () -> assertThat(partnerRel.getHolder().getPersonType()).describedAs("partner " + id + " without valid partnerRel.relHolder").isNotNull());
if (id != DELIBERATELY_BROKEN_BUSINESS_PARTNER_ID) {
logError( () -> {
assertThat(partnerRel.getContact()).describedAs("partner " + id + " without partnerRel.contact").isNotNull();
assertThat(partnerRel.getContact().getCaption()).describedAs("partner " + id + " without valid partnerRel.contact").isNotNull();
});
logError( () -> {
assertThat(partnerRel.getHolder()).describedAs("partner " + id + " without partnerRel.relHolder").isNotNull();
assertThat(partnerRel.getHolder().getPersonType()).describedAs("partner " + id + " without valid partnerRel.relHolder").isNotNull();
});
}
});
}
@ -604,6 +609,13 @@ public class ImportOfficeData extends CsvDataImport {
@Test
@Order(9000)
@ContinueOnFailure
void logCollectedErrorsBeforePersist() {
assertNoErrors();
}
@Test
@Order(9010)
void persistOfficeEntities() {
System.out.println("PERSISTING office data to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'");
@ -716,6 +728,13 @@ public class ImportOfficeData extends CsvDataImport {
);
}
@Test
@Order(9999)
@ContinueOnFailure
void logCollectedErrors() {
this.assertNoErrors();
}
private void importBusinessPartners(final String[] header, final List<String[]> records) {
final var columns = new Columns(header);

View File

@ -0,0 +1,10 @@
domain_id;domain_name;domain_since;domain_dns_master;domain_owner;valid_subdomain_names;passenger_python;passenger_nodejs;passenger_ruby;fcgi_php_bin;domainoptions
4531;l-u-g.org;2013-09-10;dns.hostsharing.net;5803;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
4532;linuxfanboysngirls.de;2013-09-10;dns.hostsharing.net;5809;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
4534;lug-mars.de;2013-09-10;dns.hostsharing.net;5809;www;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;greylisting,letsencrypt,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
4581;1981.ist-im-netz.de;2013-09-17;dns.hostsharing.net;5964;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
4587;mellis.de;2013-09-17;dns.hostsharing.net;5964;www,michael,test,photos,static,input;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;htdocsfallback,indexes,includes,letsencrypt,multiviews,cgi,fastcgi,passenger
4589;ist-im-netz.de;2013-09-17;dns.hostsharing.net;5964;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;htdocsfallback,indexes,includes,letsencrypt,multiviews,cgi,fastcgi,passenger
4600;waera.de;2013-09-17;dns.hostsharing.net;5964;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
4604;xn--wra-qla.de;2013-09-17;dns.hostsharing.net;5964;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
27662;dph-netzwerk.de;2021-06-02;h93.hostsharing.net;169596;*;/usr/bin/python3;/usr/bin/node;/usr/bin/ruby;/usr/lib/cgi-bin/php;htdocsfallback,indexes,autoconfig,greylisting,includes,letsencrypt,multiviews,cgi,fastcgi,passenger
1 domain_id domain_name domain_since domain_dns_master domain_owner valid_subdomain_names passenger_python passenger_nodejs passenger_ruby fcgi_php_bin domainoptions
2 4531 l-u-g.org 2013-09-10 dns.hostsharing.net 5803 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
3 4532 linuxfanboysngirls.de 2013-09-10 dns.hostsharing.net 5809 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
4 4534 lug-mars.de 2013-09-10 dns.hostsharing.net 5809 www /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php greylisting,letsencrypt,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
5 4581 1981.ist-im-netz.de 2013-09-17 dns.hostsharing.net 5964 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
6 4587 mellis.de 2013-09-17 dns.hostsharing.net 5964 www,michael,test,photos,static,input /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php htdocsfallback,indexes,includes,letsencrypt,multiviews,cgi,fastcgi,passenger
7 4589 ist-im-netz.de 2013-09-17 dns.hostsharing.net 5964 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php htdocsfallback,indexes,includes,letsencrypt,multiviews,cgi,fastcgi,passenger
8 4600 waera.de 2013-09-17 dns.hostsharing.net 5964 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
9 4604 xn--wra-qla.de 2013-09-17 dns.hostsharing.net 5964 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php greylisting,multiviews,indexes,htdocsfallback,includes,cgi,fastcgi,passenger
10 27662 dph-netzwerk.de 2021-06-02 h93.hostsharing.net 169596 * /usr/bin/python3 /usr/bin/node /usr/bin/ruby /usr/lib/cgi-bin/php htdocsfallback,indexes,autoconfig,greylisting,includes,letsencrypt,multiviews,cgi,fastcgi,passenger

View File

@ -5,8 +5,6 @@ emailalias_id;pac_id;name;target
2431;1112;mim00-abruf;michael.mellis@hostsharing.net
2449;1112;mim00-hhfx;"mim00-hhfx,""|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l"""
2451;1112;mim00-hhfx-l;:include:/home/pacs/mim00/etc/hhfx.list
2452;1112;mim00-empty;
2453;1112;mim00-0_entries;""
2454;1112;mim00-dev.null; /dev/null
2455;1112;mim00-1_with_space;" ""|/home/pacs/mim00/install/corpslistar/listar"""
2456;1112;mim00-1_with_single_quotes;'|/home/pacs/rir00/mailinglist/ecartis -r kybs06-intern'

1 emailalias_id pac_id name target
5 2431 1112 mim00-abruf michael.mellis@hostsharing.net
6 2449 1112 mim00-hhfx mim00-hhfx,"|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l"
7 2451 1112 mim00-hhfx-l :include:/home/pacs/mim00/etc/hhfx.list
2452 1112 mim00-empty
2453 1112 mim00-0_entries
8 2454 1112 mim00-dev.null /dev/null
9 2455 1112 mim00-1_with_space "|/home/pacs/mim00/install/corpslistar/listar"
10 2456 1112 mim00-1_with_single_quotes '|/home/pacs/rir00/mailinglist/ecartis -r kybs06-intern'

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
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

@ -0,0 +1,274 @@
{
"1981.ist-im-netz.de": {
"DOM_OWNER": "mim00",
"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": []
},
"mellis.de": {
"DOM_OWNER": "mim00",
"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.hoennig.de.",
21600,
"IN",
"CNAME",
"mih12.hostsharing.net."
],
[
"fotos.hoennig.de.",
21600,
"IN",
"CNAME",
"mih12.hostsharing.net."
],
[
"maven.hoennig.de.",
21600,
"IN",
"NS",
"dns1.hostsharing.net."
],
[
"key1._domainkey.mellis.de.",
21600,
"IN",
"TXT",
"\"v=DKIM1; k=rsa; t=s; h=sha256; s=email; \" \"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzAIotiz04KGTF9ECNxEmnnl6eTHplxxYJpOWhx3YiLbQyt6D+sN5uMa/\" \"RMIJDr5BzqzHDQPTM6esLldtIu4OpHppdu3PG4BUB8aXfA0EQvt0wQ/VFGNP36x87nfqs2L8NxbgPwhVD5RqFgj6aheTt64PB+VRco3Nc2qLF4iGpM9UlQbp/W2IITXPbLd9Z/qPo4S6Yeghsq4eFSlcNqSGyO42d23EbAxiehJKBu2eTKX5Vj+n06h1zuXOHyC5IwIe515hmS/\" \"kybbyTTEe35Rmuh+1W9aBJb85d34Thi+knUJeysFleHe7mXG7k6zFiG5HjaP7CvDzzdWvCcaJhOIqXwIDAQAB\""
]
]
},
"ist-im-netz.de": {
"DOM_OWNER": "mim00",
"TTL": 14400,
"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": [
[
"1981.ist-im-netz.de.",
14400,
"IN",
"NS",
"dns1.hostsharing.net."
],
[
"1981.ist-im-netz.de.",
14400,
"IN",
"NS",
"dns2.hostsharing.net."
],
[
"1981.ist-im-netz.de.",
14400,
"IN",
"NS",
"dns3.hostsharing.net."
]
]
},
"l-u-g.de": {
"DOM_OWNER": "lug00",
"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": []
},
"l-u-g.org": {
"DOM_OWNER": "lug00",
"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": []
},
"linuxfanboysngirls.de": {
"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-mars.de": {
"DOM_OWNER": "lug00-wla.2",
"TTL": 14400,
"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": false,
"auto-NS-RR": true,
"auto-SOA": false,
"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": [
[
"lug-mars.de.",
14400,
"IN",
"SOA",
"dns1.hostsharing.net. hostmaster.hostsharing.net. 1611590905 10800 3600 604800 3600"
],
[
"lug-mars.de.",
14400,
"IN",
"MX",
"10 mailin1.hostsharing.net."
],
[
"lug-mars.de.",
14400,
"IN",
"MX",
"20 mailin2.hostsharing.net."
],
[
"lug-mars.de.",
14400,
"IN",
"MX",
"30 mailin3.hostsharing.net."
],
[
"bbb.lug-mars.de.",
14400,
"IN",
"A",
"83.223.79.72"
],
[
"ftp.lug-mars.de.",
14400,
"IN",
"A",
"83.223.79.72"
],
[
"www.lug-mars.de.",
14400,
"IN",
"A",
"83.223.79.72"
]
]
},
"waera.de": {
"DOM_OWNER": "mim00",
"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": []
},
"xn--wra-qla.de": {
"DOM_OWNER": "mim00",
"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": []
}
}

View File

@ -0,0 +1,89 @@
{
"dph-netzwerk.de": {
"DOM_OWNER": "dph00-dph",
"TTL": 21600,
"auto-A-RR": true,
"auto-AAAA-RR": true,
"auto-AUTOCONFIG-RR": true,
"auto-AUTODISCOVER-RR": true,
"auto-DKIM-RR": false,
"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": false,
"user-RR": [
[
"dph-netzwerk.de.",
21600,
"IN",
"TXT",
"\"v=spf1 include:spf.hostsharing.net ?all\""
],
[
"*.dph-netzwerk.de.",
21600,
"IN",
"TXT",
"\"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": []
}
}