check-domain-setup-permission #97

Merged
hsh-michaelhoennig merged 17 commits from check-domain-setup-permission into master 2024-09-10 13:15:03 +02:00
2 changed files with 139 additions and 145 deletions
Showing only changes of commit b55d95645b - Show all commits

View File

@ -44,7 +44,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
if (verificationFound.isEmpty()) {
violations.add(
"[DNS] no TXT record '" + expectedTxtRecordValue +
"' found for domain name '" + domainName + "'");
"' found for domain name '" + domainName + "' (nor in its super-domain)");
}
break;
}

View File

@ -16,7 +16,9 @@ import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.naming.ServiceUnavailableException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.Map.entry;
import static java.util.Map.ofEntries;
@ -27,13 +29,18 @@ import static org.assertj.core.api.Assertions.assertThat;
class HsDomainSetupHostingAssetValidatorUnitTest {
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(final String domainName) {
final HsBookingItemRealEntity bookingItem = HsBookingItemRealEntity.builder()
public static final Dns.Result DOMAIN_NOT_REGISTERED = Dns.Result.fromException(new NameNotFoundException(
"domain not registered"));
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(
final String domainName,
final Function<HsBookingItemRealEntity.HsBookingItemRealEntityBuilder<?, ?>, HsBookingItemRealEntity> buildBookingItem) {
final HsBookingItemRealEntity bookingItem = buildBookingItem.apply(
HsBookingItemRealEntity.builder()
.type(HsBookingItemType.DOMAIN_SETUP)
.resources(new HashMap<>(ofEntries(
entry("domainName", domainName)
)))
.build();
))));
HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem);
return HsHostingAssetRbacEntity.builder()
.type(DOMAIN_SETUP)
@ -41,8 +48,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
.identifier(domainName);
}
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder() {
return validEntityBuilder("example.org");
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(final String domainName) {
return validEntityBuilder(domainName, HsBookingItemRealEntity.HsBookingItemRealEntityBuilder::build);
}
@AfterEach
@ -50,6 +57,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
Dns.resetFakeResults();
}
//=====================================================================================================================
enum InvalidDomainNameIdentifier {
EMPTY(""),
TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"),
@ -68,7 +77,12 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@EnumSource(InvalidDomainNameIdentifier.class)
void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) {
// given
final var givenEntity = validEntityBuilder().identifier(testCase.domainName).build();
final var givenEntity = validEntityBuilder(testCase.domainName,
bib -> bib.resources(new HashMap<>(ofEntries(
entry("domainName", "example.org")
))).build()
).build();
fakeValidDnsVerification(givenEntity);
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
// when
@ -98,11 +112,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
void acceptsValidIdentifier(final ValidDomainNameIdentifier testCase) {
// given
final var givenEntity = validEntityBuilder(testCase.domainName).identifier(testCase.domainName).build();
final var expectedHash = givenEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
testCase.domainName,
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
fakeValidDnsVerification(givenEntity);
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
// when
@ -112,6 +122,13 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
assertThat(result).isEmpty();
}
private static void fakeValidDnsVerification(final HsHostingAssetRbacEntity givenEntity) {
final var expectedHash = givenEntity.getBookingItem().getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
givenEntity.getIdentifier(),
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
}
@Test
void containsNoProperties() {
// when
@ -124,10 +141,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@Test
void validatesReferencedEntities() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder()
final var domainSetupHostingAssetEntity = validEntityBuilder("example.org",
bib -> bib.type(HsBookingItemType.CLOUD_SERVER).build())
.parentAsset(HsHostingAssetRealEntity.builder().type(CLOUD_SERVER).build())
.assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build())
.bookingItem(HsBookingItemRealEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
.build();
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
@ -144,9 +161,11 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@Test
void rejectsDomainNameNotMatchingBookingItemDomainName() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder()
.identifier("not-matching-booking-item-domain-name.org")
.build();
final var domainSetupHostingAssetEntity = validEntityBuilder("not-matching-booking-item-domain-name.org",
bib -> bib.resources(new HashMap<>(ofEntries(
entry("domainName", "example.org")
))).build()
).build();
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when
@ -161,10 +180,9 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@ValueSource(strings = { "not-matching-booking-item-domain-name.org", "indirect.subdomain.example.org" })
void rejectsDomainNameWhichIsNotADirectSubdomainOfParentAsset(final String newDomainName) {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder()
final var domainSetupHostingAssetEntity = validEntityBuilder(newDomainName)
.bookingItem(null)
.parentAsset(createValidParentDomainSetupAsset("example.org"))
.identifier(newDomainName)
.build();
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
@ -178,10 +196,13 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
}
@Test
void expectsEitherParentAssetOrBookingItem() {
void rejectsIfNeitherBookingItemNorParentAssetAreSet() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder().build();
final var domainSetupHostingAssetEntity = validEntityBuilder("example.org")
.bookingItem(null)
.parentAsset(null)
.build();
Dns.fakeResultForDomain(
domainSetupHostingAssetEntity.getIdentifier(),
new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null));
@ -191,7 +212,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).isEmpty();
assertThat(result).containsExactly("'DOMAIN_SETUP:example.org.bookingItem' must be of type DOMAIN_SETUP but is null");
}
enum DnsLookupFailureTestCase {
@ -222,7 +243,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
void handlesDnsLookupFailures(final DnsLookupFailureTestCase testCase) {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder().build();
final var domainSetupHostingAssetEntity = validEntityBuilder("example.org").build();
Dns.fakeResultForDomain(
domainSetupHostingAssetEntity.getIdentifier(),
Dns.Result.fromException(testCase.givenException));
@ -239,153 +260,65 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
}
}
//=====================================================================================================================
@Test
void allowSetupOfAvailableRegistrableDomain() {
// 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();
domainSetupFor("example.com").notRegistered()
.isAccepted();
}
@Test
void rejectSetupOfExistingRegistrableDomainWithoutValidDnsVerification() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder().build();
final var domainName = domainSetupHostingAssetEntity.getIdentifier();
final var expectedHash = domainSetupHostingAssetEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
domainName,
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=SOME-DEFINITELY-WRONG-HASH"));
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).contains("[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
+ "' found for domain name 'example.org'");
domainSetupFor("example.com").registered()
.isRejectedWithCauseMissingVerificationIn("example.com");
}
@Test
void allowSetupOfExistingRegistrableDomainWithValidDnsVerification() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder().build();
final var domainName = domainSetupHostingAssetEntity.getIdentifier();
final var expectedHash = domainSetupHostingAssetEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
domainName,
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).isEmpty();
domainSetupFor("example.org").registeredWithVerification()
.isAccepted();
}
@Test
void allowSetupOfUnregisteredSubdomainWithValidDnsVerificationInSuperDomain() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build();
// ... the new subdomain is not yet registered:
Dns.fakeResultForDomain(
"sub.example.org",
Dns.Result.fromException(new NameNotFoundException("domain not registered")));
// ... and a valid verification-code in the super-domain:
final var expectedHash = domainSetupHostingAssetEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
"example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).isEmpty();
domainSetupFor("sub.example.org").notRegistered().withVerificationIn("example.org")
.isAccepted();
}
@Test
void RejectSetupOfUnregisteredSubdomainWithoutDnsVerificationInSuperDomain() {
void rejectSetupOfExistingRegistrableDomainWithInvalidDnsVerification() {
domainSetupFor("example.com").registeredWithInvalidVerification()
.isRejectedWithCauseMissingVerificationIn("example.com");
}
// given
final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build();
// ... the new subdomain is not yet registered:
Dns.fakeResultForDomain(
"sub.example.org",
Dns.Result.fromException(new NameNotFoundException("domain not registered")));
// ... and a valid verification-code in the super-domain:
final var expectedHash = domainSetupHostingAssetEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
@Test
void acceptSetupOfRegisteredSubdomainWithInvalidDnsVerificationButValidDnsVerificationInSuperDomain() {
domainSetupFor("sub.example.com").registeredWithInvalidVerification().withVerificationIn("example.com")
.isAccepted();
}
// when
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).containsExactly(
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
+ "' found for domain name 'example.org'");
@Test
void rejectSetupOfUnregisteredSubdomainWithoutDnsVerificationInSuperDomain() {
domainSetupFor("sub.example.org").notRegistered()
.isRejectedWithCauseMissingVerificationIn("example.org");
}
@Test
void allowSetupOfExistingSubdomainWithValidDnsVerificationInSuperDomain() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build();
// ... the new subdomain is already registered:
Dns.fakeResultForDomain("sub.example.org", Dns.Result.fromRecords());
// ... and a valid verification-code in the super-domain:
final var expectedHash = domainSetupHostingAssetEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
"example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).isEmpty();
domainSetupFor("sub.example.org").registered()
.withVerificationIn("example.org")
.isAccepted();
}
@Test
void rejectSetupOfExistingSubdomainWithoutDnsVerification() {
// given
final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build();
// ... the new subdomain is already registered:
Dns.fakeResultForDomain("sub.example.org", Dns.Result.fromRecords());
final var expectedHash = domainSetupHostingAssetEntity.getBookingItem()
.getDirectValue("verificationCode", String.class);
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then
assertThat(result).contains("[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
+ "' found for domain name 'sub.example.org'");
domainSetupFor("sub.example.org").registered()
.isRejectedWithCauseMissingVerificationIn("sub.example.org");
}
//====================================================================================================================
private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) {
final var bookingItem = HsBookingItemRealEntity.builder()
.type(HsBookingItemType.DOMAIN_SETUP)
@ -399,4 +332,65 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
.identifier(parentDomainName).build();
return parentAsset;
}
class DomainSetupBuilder {
private final HsHostingAssetRbacEntity domainAsset;
private final String expectedHash;
public DomainSetupBuilder(final String domainName) {
domainAsset = validEntityBuilder(domainName).build();
expectedHash = domainAsset.getBookingItem().getDirectValue("verificationCode", String.class);
}
DomainSetupBuilder notRegistered() {
Dns.fakeResultForDomain(domainAsset.getIdentifier(), DOMAIN_NOT_REGISTERED);
return this;
}
DomainSetupBuilder registered() {
Dns.fakeResultForDomain(
domainAsset.getIdentifier(),
Dns.Result.fromRecords());

DomainSetup

DomainSetup
return this;
}
DomainSetupBuilder registeredWithInvalidVerification() {
Dns.fakeResultForDomain(
domainAsset.getIdentifier(),
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=SOME-DEFINITELY-WRONG-HASH"));
return this;
}
DomainSetupBuilder registeredWithVerification() {
withVerificationIn(domainAsset.getIdentifier());
return this;
}
DomainSetupBuilder withVerificationIn(final String domainName) {
Dns.fakeResultForDomain(
domainName,
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
return this;
}
void isRejectedWithCauseMissingVerificationIn(final String domainName) {
assertThat(validate()).contains(
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
+ "' found for domain name '" + domainName + "' (nor in its super-domain)");
}
void isAccepted() {
assertThat(validate()).isEmpty();
}
private List<String> validate() {
final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SETUP);
return validator.validateEntity(domainAsset);
}
}
private DomainSetupBuilder domainSetupFor(final String domainName) {
return new DomainSetupBuilder(domainName);
}
}