add DNS TXT record verification (WIP)
This commit is contained in:
parent
4a3af3f6fe
commit
e94f2f254a
@ -0,0 +1,74 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||
|
||||
import org.apache.commons.collections4.EnumerationUtils;
|
||||
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.NameNotFoundException;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.ServiceUnavailableException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public class Dns {
|
||||
|
||||
private static Result nextFakeResult = null;
|
||||
|
||||
public static void fakeNextResult(final Result fakeResult) {
|
||||
nextFakeResult = fakeResult;
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
SUCCESS,
|
||||
RECORD_TYPE_NOT_FOUND,
|
||||
NAME_NOT_FOUND,
|
||||
INVALID_NAME,
|
||||
SERVICE_UNAVAILABLE,
|
||||
UNKNOWN_FAILURE
|
||||
}
|
||||
|
||||
public record Result(Status status, List<String> records, NamingException exception) {
|
||||
}
|
||||
|
||||
private final String domainName;
|
||||
|
||||
public Dns(final String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public Result fetchRecordsOfType(final String recordType) {
|
||||
if (nextFakeResult != null) {
|
||||
try {
|
||||
return nextFakeResult;
|
||||
} finally {
|
||||
nextFakeResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final var env = new Hashtable<>();
|
||||
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
|
||||
final Attribute r = new InitialDirContext(env)
|
||||
.getAttributes(domainName, new String[] { recordType })
|
||||
.get(recordType);
|
||||
return new Result(
|
||||
r == null ? Status.RECORD_TYPE_NOT_FOUND : Status.SUCCESS,
|
||||
r == null
|
||||
? emptyList()
|
||||
: EnumerationUtils.toList(r.getAll()).stream().map(Object::toString).toList(),
|
||||
null);
|
||||
} catch (final ServiceUnavailableException e) {
|
||||
return new Result(Status.SERVICE_UNAVAILABLE, null, e);
|
||||
} catch (final NameNotFoundException e) {
|
||||
return new Result(Status.NAME_NOT_FOUND, null, e);
|
||||
} catch (InvalidNameException e) {
|
||||
return new Result(Status.INVALID_NAME, null, e);
|
||||
} catch (NamingException e) {
|
||||
return new Result(Status.UNKNOWN_FAILURE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,8 @@ 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.regex.Pattern;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
|
||||
@ -41,6 +43,40 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
|
||||
// return violations;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public List<String> validateEntity(final HsHostingAsset assetEntity) {
|
||||
|
||||
final var violations = new ArrayList<String>();
|
||||
final var result = new Dns(assetEntity.getIdentifier()).fetchRecordsOfType("TXT");
|
||||
switch ( result.status() ) {
|
||||
case Dns.Status.SUCCESS:
|
||||
final var found = result.records().stream().filter(r -> r.contains("TXT Hostsharing-domain-setup-verification=FIXME")).findAny();
|
||||
if (found.isPresent()) {
|
||||
break;
|
||||
}
|
||||
case Dns.Status.RECORD_TYPE_NOT_FOUND:
|
||||
violations.add("Domain " + assetEntity.getIdentifier() + " exists, but no record 'TXT Hostsharing-domain-setup-challenge:FIXME' found ");
|
||||
break;
|
||||
|
||||
case Dns.Status.NAME_NOT_FOUND:
|
||||
// no DNS verification necessary
|
||||
break;
|
||||
|
||||
case Dns.Status.INVALID_NAME:
|
||||
violations.add("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());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
violations.addAll(super.validateEntity(assetEntity));
|
||||
return violations;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
|
||||
if ( assetEntity.getBookingItem() != null ) {
|
||||
|
@ -9,6 +9,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
@ -249,6 +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));
|
||||
final var givenProject = realProjectRepo.findByCaption("D-1000111 default project").stream()
|
||||
.findAny().orElseThrow();
|
||||
final var bookingItem = givenSomeTemporaryBookingItem(() ->
|
||||
|
@ -18,7 +18,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HsDomainSetupHostingAssetValidatorUnitTest {
|
||||
|
||||
|
||||
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(final String domainName) {
|
||||
final HsBookingItemRealEntity bookingItem = HsBookingItemRealEntity.builder()
|
||||
.type(HsBookingItemType.DOMAIN_SETUP)
|
||||
@ -54,6 +53,7 @@ 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());
|
||||
|
||||
@ -84,6 +84,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
||||
@EnumSource(ValidDomainNameIdentifier.class)
|
||||
void acceptsValidIdentifier(final ValidDomainNameIdentifier testCase) {
|
||||
// given
|
||||
Dns.fakeNextResult(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());
|
||||
|
||||
@ -106,6 +107,7 @@ 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())
|
||||
@ -161,7 +163,9 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
|
||||
|
||||
@Test
|
||||
void expectsEitherParentAssetOrBookingItem() {
|
||||
|
||||
// given
|
||||
Dns.fakeNextResult(new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null));
|
||||
final var domainSetupHostingAssetEntity = validEntityBuilder().build();
|
||||
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
|
||||
|
||||
|
@ -69,7 +69,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
512167, // 11139, partner without contractual contact
|
||||
512170, // 11142, partner without contractual contact
|
||||
511725, // 10764, partner without contractual contact
|
||||
// 512171, // 11143, partner without partner contact -- exc
|
||||
// 512171, // 11143, partner without partner contact -- exception
|
||||
-1
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user