user-definable verificationCode and more business-level-validation-tests #100

Merged
hsh-michaelhoennig merged 8 commits from user-definable-verificationCode-and-bi-validation-tests into master 2024-09-12 10:52:45 +02:00
Showing only changes of commit 667b3908fd - Show all commits

View File

@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
@ -33,49 +33,15 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
return violations;
}
final var domainName = assetEntity.getIdentifier();
final var dnsResult = new Dns(domainName).fetchRecordsOfType("TXT");
final Supplier<String> getCode = () -> assetEntity.getBookingItem().getDirectValue("verificationCode", String.class);
final var dnsResult = new Dns(assetEntity.getIdentifier()).fetchRecordsOfType("TXT");
switch (dnsResult.status()) {
case Dns.Status.SUCCESS: {
final var expectedTxtRecordValue = "Hostsharing-domain-setup-verification-code=" + getCode.get();
final var verificationFound = findTxtRecord(dnsResult, expectedTxtRecordValue)
.or(() -> superDomain(domainName)
.flatMap(superDomainName -> findTxtRecord(
new Dns(superDomainName).fetchRecordsOfType("TXT"),
expectedTxtRecordValue))
);
if (verificationFound.isEmpty()) {
violations.add(
"[DNS] no TXT record '" + expectedTxtRecordValue +
"' found for domain name '" + domainName + "' (nor in its super-domain)");
}
case Dns.Status.SUCCESS:
violations.addAll(handleDomainNameFound(assetEntity, dnsResult));
break;
}
case Dns.Status.NAME_NOT_FOUND: {
if (isDnsVerificationRequiredForUnregisteredDomain(assetEntity)) {
final var superDomain = superDomain(domainName);
final var expectedTxtRecordValue = "Hostsharing-domain-setup-verification-code=" + getCode.get();
final var verificationFoundInSuperDomain = superDomain.map(superDomainName ->
{
final Dns.Result superDomainDnsResult = new Dns(superDomainName).fetchRecordsOfType("TXT");
if (superDomainDnsResult.status() != Dns.Status.SUCCESS) {
violations.add("[DNS] lookup failed for domain name '" + superDomainName + "': " + dnsResult.exception());
}
return superDomainDnsResult;
}
)
.flatMap(records -> findTxtRecord(records, expectedTxtRecordValue));
if (verificationFoundInSuperDomain.isEmpty()) {
violations.add(
"[DNS] no TXT record '" + expectedTxtRecordValue +
"' found for domain name '" + superDomain.orElseThrow() + "'");
}
}
// otherwise no DNS verification to be able to setup DNS for domains to register
case Dns.Status.NAME_NOT_FOUND:
violations.addAll(handleDomainNameNotFoundError(assetEntity, dnsResult));
break;
}
case Dns.Status.INVALID_NAME:
// should not happen because we validate the domain name at booking item level
@ -90,6 +56,10 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
return violations;
}
private static String verificationCode(final HsHostingAsset assetEntity) {
return assetEntity.getBookingItem().getDirectValue("verificationCode", String.class);
}
@Override
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
if (assetEntity.getBookingItem() != null) {
@ -101,6 +71,49 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
return Pattern.compile(SUBDOMAIN_NAME_REGEX + "\\." + parentDomainName.replace(".", "\\."), Pattern.CASE_INSENSITIVE);
}
private static List<String> handleDomainNameFound(final HsHostingAsset assetEntity, final Dns.Result dnsResult) {
final var violations = new ArrayList<String>();
final var expectedTxtRecordValue = "Hostsharing-domain-setup-verification-code=" + verificationCode(assetEntity);
final var verificationFound = findTxtRecord(dnsResult, expectedTxtRecordValue)
.or(() -> superDomain(assetEntity.getIdentifier())
.flatMap(superDomainName -> findTxtRecord(
new Dns(superDomainName).fetchRecordsOfType("TXT"),
expectedTxtRecordValue))
);
if (verificationFound.isEmpty()) {
violations.add(
"[DNS] no TXT record '" + expectedTxtRecordValue +
"' found for domain name '" + assetEntity.getIdentifier() + "' (nor in its super-domain)");
}
return violations;
}
private static List<String> handleDomainNameNotFoundError(final HsHostingAsset assetEntity, final Dns.Result dnsResult) {
final var violations = new ArrayList<String>();
if (isDnsVerificationRequiredForUnregisteredDomain(assetEntity)) {
final var superDomain = superDomain(assetEntity.getIdentifier());
final var expectedTxtRecordValue = "Hostsharing-domain-setup-verification-code=" + verificationCode(assetEntity);
final var verificationFoundInSuperDomain = superDomain.map(superDomainName ->
{
final Dns.Result superDomainDnsResult = new Dns(superDomainName).fetchRecordsOfType("TXT");
if (superDomainDnsResult.status() != Dns.Status.SUCCESS) {
violations.add("[DNS] lookup failed for domain name '" + superDomainName + "': " + dnsResult.exception());
}
return superDomainDnsResult;
}
)
.flatMap(records -> findTxtRecord(records, expectedTxtRecordValue));
if (verificationFoundInSuperDomain.isEmpty()) {
violations.add(
"[DNS] no TXT record '" + expectedTxtRecordValue +
"' found for domain name '" + superDomain.orElseThrow() + "'");
}
} else {
// otherwise no DNS verification to be able to setup DNS for domains to register
}
return violations;
}
private static boolean isDnsVerificationRequiredForUnregisteredDomain(final HsHostingAsset assetEntity) {
return !Dns.isRegistrableDomain(assetEntity.getIdentifier())
&& assetEntity.getParentAsset() == null;