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
4 changed files with 53 additions and 25 deletions
Showing only changes of commit 69313e560f - Show all commits

View File

@ -1,13 +1,17 @@
package net.hostsharing.hsadminng.hs.booking.item.validators; package net.hostsharing.hsadminng.hs.booking.item.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.mapper.Array;
import java.util.ArrayList;
import java.util.List;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty; import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator { class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}"; public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}";
public static final String[] REGISRTAR_LEVEL_DOMAINS = Array.of( public static final String[] REGISTRAR_LEVEL_DOMAINS = Array.of(
// "[^.]+", // top-level-domains are already rejected by FQDN_REGEX // "[^.]+", // top-level-domains are already rejected by FQDN_REGEX
"(co|org|gov|ac|sch)\\.uk", "(co|org|gov|ac|sch)\\.uk",
"(com|net|org|edu|gov|asn|id)\\.au", "(com|net|org|edu|gov|asn|id)\\.au",
@ -20,14 +24,26 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator {
"(co|net|org|govt|ac|school|geek|kiwi)\\.nz", "(co|net|org|govt|ac|school|geek|kiwi)\\.nz",
"(co|ne|or|go|re|pe)\\.kr" "(co|ne|or|go|re|pe)\\.kr"
); );
// hostsharing.com|net|org|coop, // just to be on the safe side public static final String DOMAIN_NAME_PROPERTY_NAME = "domainName";
HsDomainSetupBookingItemValidator() { HsDomainSetupBookingItemValidator() {
super( super(
stringProperty("domainName").writeOnce() stringProperty(DOMAIN_NAME_PROPERTY_NAME).writeOnce()
.matchesRegEx(FQDN_REGEX).describedAs("is not a (non-top-level) fully qualified domain name") .matchesRegEx(FQDN_REGEX).describedAs("is not a (non-top-level) fully qualified domain name")
.notMatchesRegEx(REGISRTAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name") .notMatchesRegEx(REGISTRAR_LEVEL_DOMAINS).describedAs("is a forbidden registrar-level domain name")
.required() .required()
); );
} }
@Override
public List<String> validateEntity(final HsBookingItem bookingItem) {
final var violations = new ArrayList<String>();
final var domainName = bookingItem.getDirectValue(DOMAIN_NAME_PROPERTY_NAME, String.class);
if ( !bookingItem.isLoaded() &&
domainName.matches("hostsharing.(com|net|org|coop)") ) {
violations.add("'" + bookingItem.toShortString() + ".resources." + DOMAIN_NAME_PROPERTY_NAME + "' = '" + domainName + "' is a forbidden Hostsharing domain name");
}
violations.addAll(super.validateEntity(bookingItem));
return violations;
}
} }

View File

@ -25,20 +25,6 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
public List<String> validateEntity(final HsHostingAsset assetEntity) { public List<String> validateEntity(final HsHostingAsset assetEntity) {
// TODO.impl: for newly created entities, check the permission of setting up a domain // TODO.impl: for newly created entities, check the permission of setting up a domain
// //
// reject, if the domain is any of these:
// hostsharing.com|net|org|coop, // just to be on the safe side
// [^.}+, // top-level-domain
// co.uk, org.uk, gov.uk, ac.uk, sch.uk,
// com.au, net.au, org.au, edu.au, gov.au, asn.au, id.au,
// co.jp, ne.jp, or.jp, ac.jp, go.jp,
// com.cn, net.cn, org.cn, gov.cn, edu.cn, ac.cn,
// com.br, net.br, org.br, gov.br, edu.br, mil.br, art.br,
// co.in, net.in, org.in, gen.in, firm.in, ind.in,
// com.mx, net.mx, org.mx, gob.mx, edu.mx,
// gov.it, edu.it,
// co.nz, net.nz, org.nz, govt.nz, ac.nz, school.nz, geek.nz, kiwi.nz,
// co.kr, ne.kr, or.kr, go.kr, re.kr, pe.kr
//
// allow if // allow if
// - user has Admin/Agent-role for all its sub-domains and the direct parent-Domain which are set up at at Hostsharing // - user has Admin/Agent-role for all its sub-domains and the direct parent-Domain which are set up at at Hostsharing
// - domain has DNS zone with TXT record approval // - domain has DNS zone with TXT record approval

View File

@ -11,7 +11,6 @@ import jakarta.persistence.EntityManager;
import java.util.Map; import java.util.Map;
import static java.util.Map.entry; import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.DOMAIN_SETUP;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -29,7 +28,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
@Test @Test
void acceptsUnregisteredDomain() { void acceptsUnregisteredDomain() {
// given // given
final var cloudServerBookingItemEntity = HsBookingItemRealEntity.builder() final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
.type(DOMAIN_SETUP) .type(DOMAIN_SETUP)
.project(project) .project(project)
.caption("Test-Domain") .caption("Test-Domain")
@ -39,7 +38,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.build(); .build();
// when // when
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, cloudServerBookingItemEntity); final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
// then // then
assertThat(result).isEmpty(); assertThat(result).isEmpty();
@ -48,7 +47,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
@Test @Test
void rejectsTopLevelDomain() { void rejectsTopLevelDomain() {
// given // given
final var cloudServerBookingItemEntity = HsBookingItemRealEntity.builder() final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
.type(DOMAIN_SETUP) .type(DOMAIN_SETUP)
.project(project) .project(project)
.caption("Test-Domain") .caption("Test-Domain")
@ -58,7 +57,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.build(); .build();
// when // when
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, cloudServerBookingItemEntity); final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
// then // then
assertThat(result).containsExactly("'D-12345:Test-Project:Test-Domain.resources.domainName' = 'org' is not a (non-top-level) fully qualified domain name"); assertThat(result).containsExactly("'D-12345:Test-Project:Test-Domain.resources.domainName' = 'org' is not a (non-top-level) fully qualified domain name");
@ -79,7 +78,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
}) })
void reject2ndLevelRegistrarDomain(final String secondLevelRegistrarDomain) { void reject2ndLevelRegistrarDomain(final String secondLevelRegistrarDomain) {
// given // given
final var cloudServerBookingItemEntity = HsBookingItemRealEntity.builder() final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
.type(DOMAIN_SETUP) .type(DOMAIN_SETUP)
.project(project) .project(project)
.caption("Test-Domain") .caption("Test-Domain")
@ -89,7 +88,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.build(); .build();
// when // when
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, cloudServerBookingItemEntity); final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
// then // then
assertThat(result).containsExactly( assertThat(result).containsExactly(
@ -98,6 +97,31 @@ class HsDomainSetupBookingItemValidatorUnitTest {
"' is a forbidden registrar-level domain name"); "' is a forbidden registrar-level domain name");
} }
@ParameterizedTest
@ValueSource(strings = {
"hostsharing.net", "hostsharing.org", "hostsharing.com", "hostsharing.coop"
})
void rejectHostsharingDomain(final String secondLevelRegistrarDomain) {
// given
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
.type(DOMAIN_SETUP)
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", secondLevelRegistrarDomain)
))
.build();
// when
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
// then
assertThat(result).containsExactly(
"'D-12345:Test-Project:Test-Domain.resources.domainName' = '" +
secondLevelRegistrarDomain +
"' is a forbidden Hostsharing domain name");
}
@Test @Test
void containsAllValidations() { void containsAllValidations() {
// when // when

View File

@ -256,6 +256,8 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
.project(givenProject) .project(givenProject)
.type(HsBookingItemType.DOMAIN_SETUP) .type(HsBookingItemType.DOMAIN_SETUP)
.caption("some temp domain setup booking item") .caption("some temp domain setup booking item")
.resources(Map.ofEntries(
entry("domainName", "example.org")))
.build() .build()
); );