From 8316a88bcea4df5bf613fb65af3c88e3ddf45306 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 6 Sep 2024 13:08:02 +0200 Subject: [PATCH] allowSetupOfNonExistingSubdomainOfRegistrarLevelDomain --- .../hs/hosting/asset/validators/Dns.java | 20 ++++---- .../HsDomainSetupHostingAssetValidator.java | 4 +- ...sHostingAssetControllerAcceptanceTest.java | 2 +- ...ainSetupHostingAssetValidatorUnitTest.java | 47 ++++++++++++++----- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/Dns.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/Dns.java index 63303110..353e2f5b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/Dns.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/Dns.java @@ -9,17 +9,23 @@ import javax.naming.NamingException; import javax.naming.ServiceUnavailableException; import javax.naming.directory.Attribute; import javax.naming.directory.InitialDirContext; +import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import java.util.Map; import static java.util.Collections.emptyList; public class Dns { - private static Result nextFakeResult = null; + private final static Map fakeResults = new HashMap<>(); - public static void fakeNextResult(final Result fakeResult) { - nextFakeResult = fakeResult; + public static void fakeResultForDomain(final String domainName, final Result fakeResult) { + fakeResults.put(domainName, fakeResult); + } + + static void resetFakeResults() { + fakeResults.clear(); } public enum Status { @@ -56,12 +62,8 @@ public class Dns { } public Result fetchRecordsOfType(final String recordType) { - if (nextFakeResult != null) { - try { - return nextFakeResult; - } finally { - nextFakeResult = null; - } + if (fakeResults.containsKey(domainName)) { + return fakeResults.get(domainName); } try { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java index afd4ca85..ce794398 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java @@ -60,12 +60,12 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator { break; case Dns.Status.INVALID_NAME: - violations.add("Invalid domain name " + assetEntity.getIdentifier()); + violations.add("[DNS] invalid domain name '" + assetEntity.getIdentifier() + "'"); break; case Dns.Status.SERVICE_UNAVAILABLE: case Dns.Status.UNKNOWN_FAILURE: - violations.add("DNS request for " + assetEntity.getIdentifier() + " failed: " + result.exception()); + violations.add("[DNS] lookup failed for domain name '" + assetEntity.getIdentifier() + "': " + result.exception()); break; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java index 1eaf5bbf..c1536432 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java @@ -250,7 +250,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup void globalAdmin_canAddTopLevelAsset() { context.define("superuser-alex@hostsharing.net"); - Dns.fakeNextResult(new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); + Dns.fakeResultForDomain("example.com", new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); final var givenProject = realProjectRepo.findByCaption("D-1000111 default project").stream() .findAny().orElseThrow(); final var bookingItem = givenSomeTemporaryBookingItem(() -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java index 867fd4f7..9038a052 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRbacEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -39,6 +40,11 @@ class HsDomainSetupHostingAssetValidatorUnitTest { return validEntityBuilder("example.org"); } + @AfterEach + void cleanup() { + Dns.resetFakeResults(); + } + enum InvalidDomainNameIdentifier { EMPTY(""), TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"), @@ -57,7 +63,6 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @EnumSource(InvalidDomainNameIdentifier.class) void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) { // given - Dns.fakeNextResult(new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); final var givenEntity = validEntityBuilder().identifier(testCase.domainName).build(); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); @@ -65,12 +70,11 @@ class HsDomainSetupHostingAssetValidatorUnitTest { final var result = validator.validateEntity(givenEntity); // then - assertThat(result).containsExactly( + assertThat(result).contains( "'identifier' expected to match 'example.org', but is '"+testCase.domainName+"'" ); } - enum ValidDomainNameIdentifier { SIMPLE("example.org"), MAX_LENGTH("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz01234568901.de"), @@ -88,7 +92,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @EnumSource(ValidDomainNameIdentifier.class) void acceptsValidIdentifier(final ValidDomainNameIdentifier testCase) { // given - Dns.fakeNextResult(new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); + Dns.fakeResultForDomain(testCase.domainName, new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); final var givenEntity = validEntityBuilder(testCase.domainName).identifier(testCase.domainName).build(); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); @@ -111,7 +115,6 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @Test void validatesReferencedEntities() { // given - Dns.fakeNextResult(new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); final var domainSetupHostingAssetEntity = validEntityBuilder() .parentAsset(HsHostingAssetRealEntity.builder().type(CLOUD_SERVER).build()) .assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build()) @@ -169,8 +172,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest { void expectsEitherParentAssetOrBookingItem() { // given - Dns.fakeNextResult(new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); final var domainSetupHostingAssetEntity = validEntityBuilder().build(); + Dns.fakeResultForDomain( + domainSetupHostingAssetEntity.getIdentifier(), + new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); // when @@ -183,16 +188,16 @@ class HsDomainSetupHostingAssetValidatorUnitTest { enum DnsLookupFailureTestCase { SERVICE_UNAVAILABLE( new ServiceUnavailableException("no Internet connection"), - "DNS request for example.org failed: javax.naming.ServiceUnavailableException: no Internet connection"), + "[DNS] lookup failed for domain name 'example.org': javax.naming.ServiceUnavailableException: no Internet connection"), NAME_NOT_FOUND( - new NameNotFoundException("domain not registered"), + new NameNotFoundException("domain name not found"), null), // no INVALID_NAME( new InvalidNameException("domain name too long or whatever"), - "Invalid domain name example.org"), + "[DNS] invalid domain name 'example.org'"), UNKNOWN_FAILURE( new NamingException("some other problem"), - "DNS request for example.org failed: javax.naming.NamingException: some other problem"); + "[DNS] lookup failed for domain name 'example.org': javax.naming.NamingException: some other problem"); public final NamingException givenException; public final String expectedErrorMessage; @@ -208,8 +213,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest { void handlesDnsLookupFailures(final DnsLookupFailureTestCase testCase) { // given - Dns.fakeNextResult(Dns.Result.fromException(testCase.givenException)); final var domainSetupHostingAssetEntity = validEntityBuilder().build(); + Dns.fakeResultForDomain( + domainSetupHostingAssetEntity.getIdentifier(), + Dns.Result.fromException(testCase.givenException)); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); // when @@ -223,6 +230,24 @@ class HsDomainSetupHostingAssetValidatorUnitTest { } } + @Test + void allowSetupOfNonExistingSubdomainOfRegistrarLevelDomain() { + + // given + final var domainSetupHostingAssetEntity = validEntityBuilder().build(); + final var domainName = domainSetupHostingAssetEntity.getIdentifier(); + Dns.fakeResultForDomain( + domainName, + Dns.Result.fromException(new NameNotFoundException("domain not registered"))); + final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(domainSetupHostingAssetEntity); + + // then + assertThat(result).isEmpty(); + } + private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) { final var bookingItem = HsBookingItemRealEntity.builder() .type(HsBookingItemType.DOMAIN_SETUP)