add-domain-email-setup-validation #74

Merged
hsh-michaelhoennig merged 8 commits from add-domain-email-setup-validation into master 2024-07-11 10:43:48 +02:00
6 changed files with 200 additions and 2 deletions
Showing only changes of commit 9c7f35c7de - Show all commits

View File

@ -25,6 +25,7 @@ public class HostingAssetEntityValidatorRegistry {
register(DOMAIN_HTTP_SETUP, new HsDomainHttpSetupHostingAssetValidator()); register(DOMAIN_HTTP_SETUP, new HsDomainHttpSetupHostingAssetValidator());
register(DOMAIN_EMAIL_SUBMISSION_SETUP, new HsDomainEMailSubmissionSetupHostingAssetValidator()); register(DOMAIN_EMAIL_SUBMISSION_SETUP, new HsDomainEMailSubmissionSetupHostingAssetValidator());
hsh-michaelhoennig marked this conversation as resolved Outdated

auch kürzere Namen

auch kürzere Namen
register(DOMAIN_EMAIL_MAILBOX_SETUP, new HsDomainEMailMailboxSetupHostingAssetValidator()); register(DOMAIN_EMAIL_MAILBOX_SETUP, new HsDomainEMailMailboxSetupHostingAssetValidator());
register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator());
} }
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) { private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {

View File

@ -0,0 +1,51 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.regex.Pattern;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.validation.ArrayProperty.arrayOf;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$"; // also accepts legacy pac-names
private static final String EMAIL_ADDRESS_LOCAL_PART_REGEX = "[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+"; // RFC 5322
private static final String EMAIL_ADDRESS_DOMAIN_PART_REGEX = "[a-zA-Z0-9.-]+";
private static final String EMAIL_ADDRESS_FULL_REGEX = "^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "@" + EMAIL_ADDRESS_DOMAIN_PART_REGEX + "$";
public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322
HsEMailAddressHostingAssetValidator() {
super( HsHostingAssetType.EMAIL_ADDRESS,
AlarmContact.isOptional(),
stringProperty("local-part").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").required(),
stringProperty("sub-domain").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").optional(),
arrayOf(
stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_FULL_REGEX)
).required().minLength(1));
}
@Override
public void preprocessEntity(final HsHostingAssetEntity entity) {
super.preprocessEntity(entity);
super.preprocessEntity(entity);
if (entity.getIdentifier() == null) {
entity.setIdentifier(combineIdentifier(entity));
}
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^"+ Pattern.quote(combineIdentifier(assetEntity)) + "$");
}
private static String combineIdentifier(final HsHostingAssetEntity emailAddressAssetEntity) {
return emailAddressAssetEntity.getDirectValue("local-part", String.class) +
ofNullable(emailAddressAssetEntity.getDirectValue("sub-domain", String.class)).map(s -> "." + s).orElse("") +
"@" +
emailAddressAssetEntity.getParentAsset().getIdentifier();
}
}

View File

@ -335,6 +335,34 @@ public class HsHostingAssetControllerRestTest {
"config": {} "config": {}
} }
] ]
"""),
EMAIL_ADDRESS(
List.of(
HsHostingAssetEntity.builder()
.type(HsHostingAssetType.EMAIL_ADDRESS)
.parentAsset(HsHostingAssetEntity.builder()
.type(HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_SETUP)
.identifier("example.org|MBOX")
.caption("some fake Domain-MBOX-Setup")
.build())
.identifier("office@example.org")
.caption("some fake EMail-Address")
.config(Map.ofEntries(
entry("target", Array.of("xyz00", "xyz00-abc", "office@example.com"))
))
.build()),
"""
[
{
"type": "EMAIL_ADDRESS",
"identifier": "office@example.org",
"caption": "some fake EMail-Address",
"alarmContact": null,
"config": {
"target": ["xyz00","xyz00-abc","office@example.com"]
}
}
]
"""); """);
final HsHostingAssetType assetType; final HsHostingAssetType assetType;

View File

@ -39,7 +39,9 @@ class HsHostingAssetPropsControllerAcceptanceTest {
"DOMAIN_SETUP", "DOMAIN_SETUP",
"DOMAIN_DNS_SETUP", "DOMAIN_DNS_SETUP",
"DOMAIN_HTTP_SETUP", "DOMAIN_HTTP_SETUP",
"DOMAIN_EMAIL_SUBMISSION_SETUP" "DOMAIN_EMAIL_SUBMISSION_SETUP",
"DOMAIN_EMAIL_MAILBOX_SETUP",
"EMAIL_ADDRESS"
] ]
""")); """));
// @formatter:on // @formatter:on

View File

@ -37,7 +37,9 @@ class HostingAssetEntityValidatorRegistryUnitTest {
HsHostingAssetType.DOMAIN_SETUP, HsHostingAssetType.DOMAIN_SETUP,
HsHostingAssetType.DOMAIN_DNS_SETUP, HsHostingAssetType.DOMAIN_DNS_SETUP,
HsHostingAssetType.DOMAIN_HTTP_SETUP, HsHostingAssetType.DOMAIN_HTTP_SETUP,
HsHostingAssetType.DOMAIN_EMAIL_SUBMISSION_SETUP HsHostingAssetType.DOMAIN_EMAIL_SUBMISSION_SETUP,
HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_SETUP,
HsHostingAssetType.EMAIL_ADDRESS
); );
} }
} }

View File

@ -0,0 +1,114 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.mapper.Array;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_SERVER_BOOKING_ITEM;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.EMAIL_ADDRESS;
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
import static org.assertj.core.api.Assertions.assertThat;
class HsEMailAddressHostingAssetValidatorUnitTest {
final static HsHostingAssetEntity domainEmailMailboxSetup = HsHostingAssetEntity.builder()
.type(DOMAIN_EMAIL_MAILBOX_SETUP)
.identifier("example.org")
.build();
static HsHostingAssetEntity.HsHostingAssetEntityBuilder validEntityBuilder() {
return HsHostingAssetEntity.builder()
.type(EMAIL_ADDRESS)
.parentAsset(domainEmailMailboxSetup)
.identifier("test@example.org")
.config(Map.ofEntries(
entry("local-part", "test"),
entry("target", Array.of("xyz00", "xyz00-abc", "office@example.com"))
));
}
@Test
void containsAllValidations() {
// when
final var validator = HostingAssetEntityValidatorRegistry.forType(EMAIL_ADDRESS);
// then
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=string, propertyName=local-part, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], required=true}",
"{type=string, propertyName=sub-domain, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$]}",
"{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$], maxLength=320}, required=true, minLength=1}");
}
@Test
void acceptsValidEntity() {
// given
final var emailAddressHostingAssetEntity = validEntityBuilder().build();
final var validator = HostingAssetEntityValidatorRegistry.forType(emailAddressHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(emailAddressHostingAssetEntity);
// then
assertThat(result).isEmpty();
}
@Test
void rejectsInvalidProperties() {
// given
final var emailAddressHostingAssetEntity = validEntityBuilder()
.config(Map.ofEntries(
entry("local-part", "no@allowed"),
entry("sub-domain", "no@allowedeither"),
entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com"))))
.build();
final var validator = HostingAssetEntityValidatorRegistry.forType(emailAddressHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(emailAddressHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ADDRESS:test@example.org.config.local-part' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowed' does not match",
"'EMAIL_ADDRESS:test@example.org.config.sub-domain' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowedeither' does not match",
"'EMAIL_ADDRESS:test@example.org.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$] but 'garbage' does not match any");
}
@Test
void rejectsInvalidIdentifier() {
// given
final var emailAddressHostingAssetEntity = validEntityBuilder()
.identifier("abc00-office")
.build();
final var validator = HostingAssetEntityValidatorRegistry.forType(emailAddressHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(emailAddressHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'identifier' expected to match '^\\Qtest@example.org\\E$', but is 'abc00-office'");
}
@Test
void validatesInvalidReferences() {
// given
final var emailAddressHostingAssetEntity = validEntityBuilder()
.bookingItem(TEST_MANAGED_SERVER_BOOKING_ITEM)
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
.assignedToAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
.build();
final var validator = HostingAssetEntityValidatorRegistry.forType(emailAddressHostingAssetEntity.getType());
// when
final var result = validator.validateEntity(emailAddressHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ADDRESS:test@example.org.bookingItem' must be null but is of type MANAGED_SERVER",
"'EMAIL_ADDRESS:test@example.org.parentAsset' must be of type DOMAIN_EMAIL_MAILBOX_SETUP but is of type MANAGED_SERVER",
"'EMAIL_ADDRESS:test@example.org.assignedToAsset' must be null but is of type MANAGED_SERVER");
}
}