user-definable verificationCode and more business-level-validation-tests #100
@ -25,8 +25,9 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
|
|||||||
.notMatchesRegEx(REGISTRAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name")
|
.notMatchesRegEx(REGISTRAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name")
|
||||||
.required(),
|
.required(),
|
||||||
stringProperty(VERIFICATION_CODE_PROPERTY_NAME)
|
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 net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
|
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 {
|
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";
|
public static final String DOMAIN_NAME_PROPERTY_NAME = "domainName";
|
||||||
|
|
||||||
HsDomainSetupHostingAssetValidator() {
|
HsDomainSetupHostingAssetValidator() {
|
||||||
@ -34,44 +33,18 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
|||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var domainName = assetEntity.getIdentifier();
|
final var dnsResult = new Dns(assetEntity.getIdentifier()).fetchRecordsOfType("TXT");
|
||||||
final var dnsResult = new Dns(domainName).fetchRecordsOfType("TXT");
|
|
||||||
final Supplier<String> getCode = () -> assetEntity.getBookingItem().getDirectValue("verificationCode", String.class);
|
|
||||||
switch (dnsResult.status()) {
|
switch (dnsResult.status()) {
|
||||||
case Dns.Status.SUCCESS: {
|
case Dns.Status.SUCCESS:
|
||||||
final var expectedTxtRecordValue = "Hostsharing-domain-setup-verification-code=" + getCode.get();
|
violations.addAll(handleDomainNameFound(assetEntity, dnsResult));
|
||||||
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)");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case Dns.Status.NAME_NOT_FOUND: {
|
case Dns.Status.NAME_NOT_FOUND:
|
||||||
if (isDnsVerificationRequiredForUnregisteredDomain(assetEntity)) {
|
violations.addAll(handleDomainNameNotFoundError(assetEntity, dnsResult));
|
||||||
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
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case Dns.Status.INVALID_NAME:
|
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() + "'");
|
violations.add("[DNS] invalid domain name '" + assetEntity.getIdentifier() + "'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -83,6 +56,10 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
|||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String verificationCode(final HsHostingAsset assetEntity) {
|
||||||
|
return assetEntity.getBookingItem().getDirectValue("verificationCode", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
|
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
|
||||||
if (assetEntity.getBookingItem() != null) {
|
if (assetEntity.getBookingItem() != null) {
|
||||||
@ -94,6 +71,49 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
|||||||
return Pattern.compile(SUBDOMAIN_NAME_REGEX + "\\." + parentDomainName.replace(".", "\\."), Pattern.CASE_INSENSITIVE);
|
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) {
|
private static boolean isDnsVerificationRequiredForUnregisteredDomain(final HsHostingAsset assetEntity) {
|
||||||
return !Dns.isRegistrableDomain(assetEntity.getIdentifier())
|
return !Dns.isRegistrableDomain(assetEntity.getIdentifier())
|
||||||
&& assetEntity.getParentAsset() == null;
|
&& assetEntity.getParentAsset() == null;
|
||||||
|
@ -266,7 +266,7 @@ public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T
|
|||||||
|
|
||||||
private boolean isSpecPotentiallyComplete() {
|
private boolean isSpecPotentiallyComplete() {
|
||||||
return required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && !writeOnly
|
return required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && !writeOnly
|
||||||
&& defaultValue == null;
|
&& defaultValue == null && computedBy == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -28,7 +28,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void acceptsRegisterableDomain() {
|
void acceptsRegisterableDomainWithGeneratedVerificationCode() {
|
||||||
// given
|
// given
|
||||||
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
||||||
.type(DOMAIN_SETUP)
|
.type(DOMAIN_SETUP)
|
||||||
@ -46,6 +46,26 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
assertThat(result).isEmpty();
|
assertThat(result).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acceptsRegisterableDomainWithExplicitVerificationCode() {
|
||||||
|
// given
|
||||||
|
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
||||||
|
.type(DOMAIN_SETUP)
|
||||||
|
.project(project)
|
||||||
|
.caption("Test-Domain")
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("domainName", "example.org"),
|
||||||
|
entry("verificationCode", "1234-5678-9100")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void acceptsMaximumDomainNameLength() {
|
void acceptsMaximumDomainNameLength() {
|
||||||
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
|
||||||
@ -150,6 +170,6 @@ class HsDomainSetupBookingItemValidatorUnitTest {
|
|||||||
// then
|
// then
|
||||||
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||||
"{type=string, propertyName=domainName, matchesRegEx=[^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}], matchesRegExDescription=is not a (non-top-level) fully qualified domain name, notMatchesRegEx=[[^.]+, (co|org|gov|ac|sch)\\.uk, (com|net|org|edu|gov|asn|id)\\.au, (co|ne|or|ac|go)\\.jp, (com|net|org|gov|edu|ac)\\.cn, (com|net|org|gov|edu|mil|art)\\.br, (co|net|org|gen|firm|ind)\\.in, (com|net|org|gob|edu)\\.mx, (gov|edu)\\.it, (co|net|org|govt|ac|school|geek|kiwi)\\.nz, (co|ne|or|go|re|pe)\\.kr], notMatchesRegExDescription=is a forbidden registrar-level domain name, maxLength=253, required=true, writeOnce=true}",
|
"{type=string, propertyName=domainName, matchesRegEx=[^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}], matchesRegExDescription=is not a (non-top-level) fully qualified domain name, notMatchesRegEx=[[^.]+, (co|org|gov|ac|sch)\\.uk, (com|net|org|edu|gov|asn|id)\\.au, (co|ne|or|ac|go)\\.jp, (com|net|org|gov|edu|ac)\\.cn, (com|net|org|gov|edu|mil|art)\\.br, (co|net|org|gen|firm|ind)\\.in, (com|net|org|gob|edu)\\.mx, (gov|edu)\\.it, (co|net|org|govt|ac|school|geek|kiwi)\\.nz, (co|ne|or|go|re|pe)\\.kr], notMatchesRegExDescription=is a forbidden registrar-level domain name, maxLength=253, required=true, writeOnce=true}",
|
||||||
"{type=string, propertyName=verificationCode, readOnly=true, computed=IN_INIT}");
|
"{type=string, propertyName=verificationCode, minLength=12, maxLength=64, computed=IN_INIT}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRbacEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRbacEntity;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@ -35,8 +36,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(
|
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(
|
||||||
final String domainName,
|
final String domainName,
|
||||||
final Function<HsBookingItemRealEntity.HsBookingItemRealEntityBuilder<?, ?>, HsBookingItemRealEntity> buildBookingItem) {
|
final Function<HsBookingItemRealEntity.HsBookingItemRealEntityBuilder<?, ?>, HsBookingItemRealEntity> buildBookingItem) {
|
||||||
final HsBookingItemRealEntity bookingItem = buildBookingItem.apply(
|
final var project = HsBookingProjectRealEntity.builder().build();
|
||||||
|
final var bookingItem = buildBookingItem.apply(
|
||||||
HsBookingItemRealEntity.builder()
|
HsBookingItemRealEntity.builder()
|
||||||
|
.project(project)
|
||||||
.type(HsBookingItemType.DOMAIN_SETUP)
|
.type(HsBookingItemType.DOMAIN_SETUP)
|
||||||
.resources(new HashMap<>(ofEntries(
|
.resources(new HashMap<>(ofEntries(
|
||||||
entry("domainName", domainName)
|
entry("domainName", domainName)
|
||||||
@ -90,7 +93,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).contains(
|
assertThat(result).contains(
|
||||||
"'identifier' expected to match '(\\*|(?!-)[A-Za-z0-9-]{1,63}(?<!-))\\.example\\.org', but is '" + testCase.domainName + "'"
|
"'identifier' expected to match '(\\*|(?!-)[A-Za-z0-9-]{1,63}(?<!-))\\.example\\.org', but is '"
|
||||||
|
+ testCase.domainName + "'"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,8 +145,9 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void validatesReferencedEntities() {
|
void validatesReferencedEntities() {
|
||||||
// given
|
// given
|
||||||
final var domainSetupHostingAssetEntity = validEntityBuilder("example.org",
|
final var domainSetupHostingAssetEntity = validEntityBuilder(
|
||||||
bib -> bib.type(HsBookingItemType.CLOUD_SERVER).build())
|
"example.org",
|
||||||
|
bib -> bib.type(HsBookingItemType.CLOUD_SERVER).build())
|
||||||
.parentAsset(HsHostingAssetRealEntity.builder().type(CLOUD_SERVER).build())
|
.parentAsset(HsHostingAssetRealEntity.builder().type(CLOUD_SERVER).build())
|
||||||
.assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build())
|
.assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build())
|
||||||
.build();
|
.build();
|
||||||
@ -161,11 +166,12 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void rejectsDomainNameNotMatchingBookingItemDomainName() {
|
void rejectsDomainNameNotMatchingBookingItemDomainName() {
|
||||||
// given
|
// given
|
||||||
final var domainSetupHostingAssetEntity = validEntityBuilder("not-matching-booking-item-domain-name.org",
|
final var domainSetupHostingAssetEntity = validEntityBuilder(
|
||||||
bib -> bib.resources(new HashMap<>(ofEntries(
|
"not-matching-booking-item-domain-name.org",
|
||||||
entry("domainName", "example.org")
|
bib -> bib.resources(new HashMap<>(ofEntries(
|
||||||
))).build()
|
entry("domainName", "example.org")
|
||||||
).build();
|
))).build()
|
||||||
|
).build();
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
|
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -262,6 +268,24 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
|
|
||||||
//=====================================================================================================================
|
//=====================================================================================================================
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsSetupOfRegistrar1stLevelDomain() {
|
||||||
|
domainSetupFor("org").notRegistered()
|
||||||
|
.isRejectedWithCauseForbidden("registrar-level domain name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsSetupOfRegistrar2ndLevelDomain() {
|
||||||
|
domainSetupFor("co.uk").notRegistered()
|
||||||
|
.isRejectedWithCauseForbidden("registrar-level domain name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsSetupOfHostsharingDomain() {
|
||||||
|
domainSetupFor("hostsharing.net").notRegistered()
|
||||||
|
.isRejectedWithCauseForbidden("Hostsharing domain name");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void allowSetupOfAvailableRegistrableDomain() {
|
void allowSetupOfAvailableRegistrableDomain() {
|
||||||
domainSetupFor("example.com").notRegistered()
|
domainSetupFor("example.com").notRegistered()
|
||||||
@ -298,6 +322,18 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
.isAccepted();
|
.isAccepted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void allowSetupOfUnregisteredSubdomainIfSuperDomainParentAssetIsSpecified() {
|
||||||
|
domainSetupFor("sub.example.org").notRegistered().withParentAsset("example.org")
|
||||||
|
.isAccepted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectSetupOfUnregisteredSubdomainIfWrongParentAssetIsSpecified() {
|
||||||
|
domainSetupFor("sub.example.org").notRegistered().withParentAsset("example.net")
|
||||||
|
.isRejectedDueToInvalidIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void allowSetupOfUnregisteredSubdomainWithValidDnsVerificationInSuperDomain() {
|
void allowSetupOfUnregisteredSubdomainWithValidDnsVerificationInSuperDomain() {
|
||||||
domainSetupFor("sub.example.org").notRegistered().withVerificationIn("example.org")
|
domainSetupFor("sub.example.org").notRegistered().withVerificationIn("example.org")
|
||||||
@ -322,6 +358,12 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
.isRejectedWithCauseMissingVerificationIn("example.org");
|
.isRejectedWithCauseMissingVerificationIn("example.org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectSetupOfUnregisteredSubdomainOfUnregisteredSuperDomain() {
|
||||||
|
domainSetupFor("sub.sub.example.org").notRegistered()
|
||||||
|
.isRejectedWithCauseDomainNameNotFound("sub.example.org");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void acceptSetupOfUnregisteredSubdomainWithParentAssetEvenWithoutDnsVerificationInSuperDomain() {
|
void acceptSetupOfUnregisteredSubdomainWithParentAssetEvenWithoutDnsVerificationInSuperDomain() {
|
||||||
domainSetupWithParentAssetFor("sub.example.org").notRegistered()
|
domainSetupWithParentAssetFor("sub.example.org").notRegistered()
|
||||||
@ -341,6 +383,20 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
.isRejectedWithCauseMissingVerificationIn("sub.example.org");
|
.isRejectedWithCauseMissingVerificationIn("sub.example.org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void allowSetupOfRegistrableDomainWithUserDefinedVerificationCode() {
|
||||||
|
domainSetupFor("example.edu.it").notRegistered().withUserDefinedVerificationCode("ABCD-EFGH-IJKL-MNOP")
|
||||||
|
.withVerificationIn("example.edu.it")
|
||||||
|
.isAccepted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectSetupOfRegistrableDomainWithInvalidUserDefinedVerificationCode() {
|
||||||
|
domainSetupFor("example.edu.it").notRegistered().withUserDefinedVerificationCode("ABCD-EFGH-IJKL-MNOP")
|
||||||
|
.withVerificationIn("example.edu.it", "SOME-OTHER-CODE")
|
||||||
|
.isRejectedWithCauseMissingVerificationIn("example.edu.it");
|
||||||
|
}
|
||||||
|
|
||||||
//====================================================================================================================
|
//====================================================================================================================
|
||||||
|
|
||||||
private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) {
|
private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) {
|
||||||
@ -360,11 +416,9 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
class DomainSetupBuilder {
|
class DomainSetupBuilder {
|
||||||
|
|
||||||
private final HsHostingAssetRbacEntity domainAsset;
|
private final HsHostingAssetRbacEntity domainAsset;
|
||||||
private final String expectedHash;
|
|
||||||
|
|
||||||
public DomainSetupBuilder(final String domainName) {
|
public DomainSetupBuilder(final String domainName) {
|
||||||
domainAsset = validEntityBuilder(domainName).build();
|
domainAsset = validEntityBuilder(domainName).build();
|
||||||
expectedHash = domainAsset.getBookingItem().getDirectValue("verificationCode", String.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DomainSetupBuilder(final HsHostingAssetRealEntity parentAsset, final String domainName) {
|
public DomainSetupBuilder(final HsHostingAssetRealEntity parentAsset, final String domainName) {
|
||||||
@ -372,7 +426,6 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
.bookingItem(null)
|
.bookingItem(null)
|
||||||
.parentAsset(parentAsset)
|
.parentAsset(parentAsset)
|
||||||
.build();
|
.build();
|
||||||
expectedHash = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainSetupBuilder notRegistered() {
|
DomainSetupBuilder notRegistered() {
|
||||||
@ -399,30 +452,79 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainSetupBuilder withVerificationIn(final String domainName) {
|
DomainSetupBuilder withUserDefinedVerificationCode(final String verificationCode) {
|
||||||
assertThat(expectedHash).as("no expectedHash available").isNotNull();
|
domainAsset.getBookingItem().getResources().put("verificationCode", verificationCode);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainSetupBuilder withVerificationIn(final String domainName, final String verificationCode) {
|
||||||
|
assertThat(verificationCode).as("explicit verificationCode must not be null").isNotNull();
|
||||||
Dns.fakeResultForDomain(
|
Dns.fakeResultForDomain(
|
||||||
domainName,
|
domainName,
|
||||||
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
|
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + verificationCode));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainSetupBuilder withVerificationIn(final String domainName) {
|
||||||
|
assertThat(expectedVerificationCode()).as("no expectedHash available").isNotNull();
|
||||||
|
Dns.fakeResultForDomain(
|
||||||
|
domainName,
|
||||||
|
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedVerificationCode()));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void isRejectedWithCauseMissingVerificationIn(final String domainName) {
|
void isRejectedWithCauseMissingVerificationIn(final String domainName) {
|
||||||
assertThat(expectedHash).as("no expectedHash available").isNotNull();
|
assertThat(expectedVerificationCode()).as("no expectedHash available").isNotNull();
|
||||||
assertThat(validate()).containsAnyOf(
|
assertThat(validate()).containsAnyOf(
|
||||||
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
|
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedVerificationCode()
|
||||||
+ "' found for domain name '" + domainName + "' (nor in its super-domain)",
|
+ "' found for domain name '" + domainName + "' (nor in its super-domain)",
|
||||||
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
|
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedVerificationCode()
|
||||||
+ "' found for domain name '" + domainName + "'");
|
+ "' found for domain name '" + domainName + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void isRejectedWithCauseForbidden(final String type) {
|
||||||
|
assertThat(validate()).contains(
|
||||||
|
"'D-???????:null:null.resources.domainName' = '" + domainAsset.getIdentifier() + "' is a forbidden " + type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void isRejectedDueToInvalidIdentifier() {
|
||||||
|
assertThat(validate()).contains(
|
||||||
|
"'identifier' expected to match '(\\*|(?!-)[A-Za-z0-9-]{1,63}(?<!-))\\.example\\.net', but is 'sub.example.org'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void isRejectedWithCauseDomainNameNotFound(final String domainName) {
|
||||||
|
assertThat(validate()).contains(
|
||||||
|
"[DNS] lookup failed for domain name '" + domainName + "': javax.naming.NameNotFoundException: domain not registered"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void isAccepted() {
|
void isAccepted() {
|
||||||
assertThat(validate()).isEmpty();
|
assertThat(validate()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String expectedVerificationCode() {
|
||||||
|
return domainAsset.getBookingItem().getDirectValue("verificationCode", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> validate() {
|
private List<String> validate() {
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SETUP);
|
if ( domainAsset.getBookingItem() != null ) {
|
||||||
return validator.validateEntity(domainAsset);
|
final var biValidation = HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP)
|
||||||
|
.validateEntity(domainAsset.getBookingItem());
|
||||||
|
if (!biValidation.isEmpty()) {
|
||||||
|
return biValidation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HostingAssetEntityValidatorRegistry.forType(DOMAIN_SETUP)
|
||||||
|
.validateEntity(domainAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainSetupBuilder withParentAsset(final String parentAssetDomainName) {
|
||||||
|
domainAsset.setBookingItem(null);
|
||||||
|
domainAsset.setParentAsset(HsHostingAssetRealEntity.builder().type(DOMAIN_SETUP).identifier(parentAssetDomainName).build());
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +534,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
|||||||
|
|
||||||
private DomainSetupBuilder domainSetupWithParentAssetFor(final String domainName) {
|
private DomainSetupBuilder domainSetupWithParentAssetFor(final String domainName) {
|
||||||
return new DomainSetupBuilder(
|
return new DomainSetupBuilder(
|
||||||
HsHostingAssetRealEntity.builder().type(DOMAIN_SETUP).identifier(Dns.superDomain(domainName).orElseThrow()).build(),
|
HsHostingAssetRealEntity.builder()
|
||||||
|
.type(DOMAIN_SETUP)
|
||||||
|
.identifier(Dns.superDomain(domainName).orElseThrow())
|
||||||
|
.build(),
|
||||||
domainName);
|
domainName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user