user-definable verificationCode and more business-level-validation-tests (#100)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/100 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
@@ -25,8 +25,9 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
||||
.notMatchesRegEx(REGISTRAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name")
|
||||
.required(),
|
||||
stringProperty(VERIFICATION_CODE_PROPERTY_NAME)
|
||||
.readOnly().initializedBy(HsDomainSetupBookingItemValidator::generateVerificationCode)
|
||||
|
||||
.minLength(12)
|
||||
.maxLength(64)
|
||||
.initializedBy(HsDomainSetupBookingItemValidator::generateVerificationCode)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
@@ -13,7 +13,6 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainHttp
|
||||
|
||||
class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
||||
|
||||
public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}";
|
||||
public static final String DOMAIN_NAME_PROPERTY_NAME = "domainName";
|
||||
|
||||
HsDomainSetupHostingAssetValidator() {
|
||||
@@ -34,44 +33,18 @@ 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.flatMap(superDomainName -> findTxtRecord(
|
||||
new Dns(superDomainName).fetchRecordsOfType("TXT"),
|
||||
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
|
||||
violations.add("[DNS] invalid domain name '" + assetEntity.getIdentifier() + "'");
|
||||
break;
|
||||
|
||||
@@ -83,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) {
|
||||
@@ -94,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;
|
||||
|
@@ -266,7 +266,7 @@ public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T
|
||||
|
||||
private boolean isSpecPotentiallyComplete() {
|
||||
return required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && !writeOnly
|
||||
&& defaultValue == null;
|
||||
&& defaultValue == null && computedBy == null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
Reference in New Issue
Block a user