import-email-addresses #86

Merged
hsh-michaelhoennig merged 18 commits from import-email-addresses into master 2024-08-12 12:06:12 +02:00
6 changed files with 36 additions and 14 deletions
Showing only changes of commit c136064ff7 - Show all commits

View File

@ -6,8 +6,10 @@ import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAsse
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.regex.Pattern;
/** /**
* Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsHostingAsset into a readable API. * Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsHostingAsset into a readable API.
@ -40,12 +42,14 @@ public class HostingAssetEntitySaveProcessor {
return this; return this;
} }
// TODO.impl: remove once the migration of legacy data is done
/// validates the entity itself including its properties, but ignoring some error messages for import of legacy data /// validates the entity itself including its properties, but ignoring some error messages for import of legacy data
public HostingAssetEntitySaveProcessor validateEntityIgnoring(final String ignoreRegExp) { public HostingAssetEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) {
step("validateEntity", "prepareForSave"); step("validateEntity", "prepareForSave");
final var ignoreRegExpPatterns = Arrays.stream(ignoreRegExp).map(Pattern::compile).toList();
MultiValidationException.throwIfNotEmpty( MultiValidationException.throwIfNotEmpty(
validator.validateEntity(entity).stream() validator.validateEntity(entity).stream()
.filter(errorMsg -> !errorMsg.matches(ignoreRegExp)) .filter(error -> ignoreRegExpPatterns.stream().noneMatch(p -> p.matcher(error).matches() ))
.toList() .toList()
); );
return this; return this;

View File

@ -11,11 +11,12 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator { class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$"; // also accepts legacy pac-names private static final String TARGET_MAILBOX_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][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_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_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 + "$"; private static final String EMAIL_ADDRESS_FULL_REGEX = "^(" + EMAIL_ADDRESS_LOCAL_PART_REGEX + ")?@" + EMAIL_ADDRESS_DOMAIN_PART_REGEX + "$";
private static final String NOBODY_REGEX = "^nobody$"; private static final String NOBODY_REGEX = "^nobody$";
private static final String DEVNULL_REGEX = "^/dev/null$";
public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322 public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322
HsEMailAddressHostingAssetValidator() { HsEMailAddressHostingAssetValidator() {
@ -25,7 +26,7 @@ class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
stringProperty("local-part").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").writeOnce().optional(), stringProperty("local-part").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").writeOnce().optional(),
stringProperty("sub-domain").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").writeOnce().optional(), stringProperty("sub-domain").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").writeOnce().optional(),
arrayOf( arrayOf(
stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_FULL_REGEX, NOBODY_REGEX) stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(TARGET_MAILBOX_REGEX, EMAIL_ADDRESS_FULL_REGEX, NOBODY_REGEX, DEVNULL_REGEX)
).required().minLength(1)); ).required().minLength(1));
} }

View File

@ -33,7 +33,13 @@ class HsEMailAddressHostingAssetValidatorUnitTest {
.identifier("old-local-part@example.org") .identifier("old-local-part@example.org")
.config(new HashMap<>(ofEntries( .config(new HashMap<>(ofEntries(
entry("local-part", "old-local-part"), entry("local-part", "old-local-part"),
entry("target", Array.of("xyz00", "xyz00-abc", "office@example.com")) entry("target", Array.of(
"xyz00",
"xyz00-abc",
"xyz00-xyz+list",
"office@example.com",
"/dev/null"
))
))); )));
} }
@ -46,7 +52,7 @@ class HsEMailAddressHostingAssetValidatorUnitTest {
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=string, propertyName=local-part, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], writeOnce=true}", "{type=string, propertyName=local-part, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], writeOnce=true}",
"{type=string, propertyName=sub-domain, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], writeOnce=true}", "{type=string, propertyName=sub-domain, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], writeOnce=true}",
"{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+)?@[a-zA-Z0-9.-]+$, ^nobody$], maxLength=320}, required=true, minLength=1}"); "{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\.+_-]*)?$, ^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+)?@[a-zA-Z0-9.-]+$, ^nobody$, ^/dev/null$], maxLength=320}, required=true, minLength=1}");
} }
@Test @Test
@ -69,7 +75,11 @@ class HsEMailAddressHostingAssetValidatorUnitTest {
.config(new HashMap<>(ofEntries( .config(new HashMap<>(ofEntries(
entry("local-part", "no@allowed"), entry("local-part", "no@allowed"),
entry("sub-domain", "no@allowedeither"), entry("sub-domain", "no@allowedeither"),
entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com"))))) entry("target", Array.of(
"xyz00",
"xyz00-abc",
"garbage",
"office@example.com")))))
.build(); .build();
final var validator = HostingAssetEntityValidatorRegistry.forType(emailAddressHostingAssetEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(emailAddressHostingAssetEntity.getType());
@ -80,7 +90,7 @@ class HsEMailAddressHostingAssetValidatorUnitTest {
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ADDRESS:old-local-part@example.org.config.local-part' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowed' does not match", "'EMAIL_ADDRESS:old-local-part@example.org.config.local-part' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowed' does not match",
"'EMAIL_ADDRESS:old-local-part@example.org.config.sub-domain' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowedeither' does not match", "'EMAIL_ADDRESS:old-local-part@example.org.config.sub-domain' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowedeither' does not match",
"'EMAIL_ADDRESS:old-local-part@example.org.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+)?@[a-zA-Z0-9.-]+$, ^nobody$] but 'garbage' does not match any"); "'EMAIL_ADDRESS:old-local-part@example.org.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\.+_-]*)?$, ^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+)?@[a-zA-Z0-9.-]+$, ^nobody$, ^/dev/null$] but 'garbage' does not match any");
} }
@Test @Test

View File

@ -20,6 +20,7 @@ import org.springframework.transaction.support.TransactionTemplate;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ValidationException;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -309,7 +310,7 @@ public class CsvDataImport extends ContextBasedTest {
void logError(final Runnable assertion) { void logError(final Runnable assertion) {
try { try {
assertion.run(); assertion.run();
} catch (final AssertionError exc) { } catch (final AssertionError | ValidationException exc) {
logError(exc.getMessage()); logError(exc.getMessage());
} }
} }

View File

@ -668,6 +668,9 @@ public class ImportHostingAssets extends ImportOfficeData {
if (isImportingControlledTestData()) { if (isImportingControlledTestData()) {
expectError("zonedata dom_owner of mellis.de is old00 but expected to be mim00"); expectError("zonedata dom_owner of mellis.de is old00 but expected to be mim00");
expectError("\nexpected: \"vm1068\"\n but was: \"vm1093\""); expectError("\nexpected: \"vm1068\"\n but was: \"vm1093\"");
expectError("['EMAIL_ADDRESS:webmaster@hamburg-west.l-u-g.org.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\.+_-]*)?$, ^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+)?@[a-zA-Z0-9.-]+$, ^nobody$, ^/dev/null$] but 'raoul.lottmann@example.com peter.lottmann@example.com' does not match any]");
expectError("['EMAIL_ADDRESS:abuse@mellis.de.config.target' length is expected to be at min 1 but length of [[]] is 0]");
expectError("['EMAIL_ADDRESS:abuse@ist-im-netz.de.config.target' length is expected to be at min 1 but length of [[]] is 0]");
} }
this.assertNoErrors(); this.assertNoErrors();
} }
@ -920,7 +923,10 @@ public class ImportHostingAssets extends ImportOfficeData {
logError(() -> logError(() ->
new HostingAssetEntitySaveProcessor(em, entry.getValue()) new HostingAssetEntitySaveProcessor(em, entry.getValue())
.preprocessEntity() .preprocessEntity()
.validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*") .validateEntityIgnoring(
"'EMAIL_ALIAS:.*\\.config\\.target' .*",
"'EMAIL_ADDRESS:.*\\.config\\.target' .*"
)
.prepareForSave() .prepareForSave()
.saveUsing(entity -> persist(entry.getKey(), entity)) .saveUsing(entity -> persist(entry.getKey(), entity))
.validateContext() .validateContext()

View File

@ -12,7 +12,7 @@ emailaddr_id;domain_id;localpart;subdomain;target
54760;4531;info;hamburg-west;peter.lottmann@example.com 54760;4531;info;hamburg-west;peter.lottmann@example.com
54761;4531;lugmaster;hamburg-west;raoul.lottmann@example.com 54761;4531;lugmaster;hamburg-west;raoul.lottmann@example.com
54762;4531;postmaster;hamburg-west;raoul.lottmann@example.com 54762;4531;postmaster;hamburg-west;raoul.lottmann@example.com
54763;4531;webmaster;hamburg-west;raoul.lottmann@example.com 54763;4531;webmaster;hamburg-west;raoul.lottmann@example.com peter.lottmann@example.com
54764;4531;;eliza;eliza@example.net 54764;4531;;eliza;eliza@example.net
54765;4531;;;lug00 54765;4531;;;lug00
54766;4532;;;nomail 54766;4532;;;nomail
@ -22,10 +22,10 @@ emailaddr_id;domain_id;localpart;subdomain;target
54963;4581;abuse;;mim00 54963;4581;abuse;;mim00
54964;4581;postmaster;;mim00 54964;4581;postmaster;;mim00
54965;4581;webmaster;;mim00 54965;4581;webmaster;;mim00
54981;4587;abuse;;mim00 54981;4587;abuse;;
54982;4587;postmaster;;mim00 54982;4587;postmaster;;/dev/null
54983;4587;webmaster;;mim00 54983;4587;webmaster;;mim00
54987;4589;abuse;;mim00 54987;4589;abuse;;""
54988;4589;postmaster;;mim00 54988;4589;postmaster;;mim00
54989;4589;webmaster;;mim00 54989;4589;webmaster;;mim00
55020;4600;abuse;;mim00 55020;4600;abuse;;mim00

1 emailaddr_id domain_id localpart subdomain target
2 54746 4531 abuse lug00
3 54747 4531 postmaster nomail
4 54748 4531 webmaster bounce
5 54745 4531 lugmaster nobody
6 54749 4532 abuse lug00-mars
7 54750 4532 postmaster m.hinsel@example.org
8 54751 4532 webmaster m.hinsel@example.org
9 54755 4534 abuse lug00-marl
10 54756 4534 postmaster m.hinsel@example.org
11 54757 4534 webmaster m.hinsel@example.org
12 54760 4531 info hamburg-west peter.lottmann@example.com
13 54761 4531 lugmaster hamburg-west raoul.lottmann@example.com
14 54762 4531 postmaster hamburg-west raoul.lottmann@example.com
15 54763 4531 webmaster hamburg-west raoul.lottmann@example.com peter.lottmann@example.com
16 54764 4531 eliza eliza@example.net
17 54765 4531 lug00
18 54766 4532 nomail
19 54767 4532 hostmaster hostmaster@example.net
20 54795 4534 bounce
21 54796 4534 hostmaster hostmaster@example.net
22 54963 4581 abuse mim00
23 54964 4581 postmaster mim00
24 54965 4581 webmaster mim00
25 54981 4587 abuse
26 54982 4587 postmaster /dev/null
27 54983 4587 webmaster mim00
28 54987 4589 abuse
29 54988 4589 postmaster mim00
30 54989 4589 webmaster mim00
31 55020 4600 abuse mim00
32 55021 4600 postmaster mim00
33 55022 4600 webmaster mim00
34 55032 4604 abuse mim00
35 55033 4604 postmaster mim00
36 55034 4604 webmaster mim00
37 55037 4587 eberhard eberhard@mellis.de
38 55038 4587 marleen marleen@mellis.de
39 55039 4587 michael mh@dump.mellis.de
40 55040 4587 lists michael mim00-lists
41 55041 4587 ooo michael mim00-ooo
42 55043 4587 test test@mellis.de
43 55044 4587 trap mim00-spam
44 55046 4587 anke anke@segelschule-jade.de
45 55052 4587 eberhard mellis@example.org
46 55053 4587 gitti gitta.mellis@gmx.de
47 55054 4587 imap mim00-imap
48 55057 4587 listar mim00-listar
49 55059 4587 marleen marleen.mellis@t-online.de
50 55060 4587 mime mh@dump.mellis.de
51 55061 4587 michael mh@dump.mellis.de
52 55062 4587 monika nomail
53 55063 4587 nobody nobody
54 55064 4587 palm mim00-imap
55 55065 4587 procmail mim00-mail
56 55066 4587 reporter.web.de nomail
57 55067 4587 script mim00-script
58 55068 4587 spamtrap mim00-spamtrap
59 55069 4587 susanne susanne.mellis@example.net
60 55070 4587 test mim00-test
61 55071 4587 ursula 01234wasauchimmer@example.net
62 55072 4587 webcmstag mim00-webcmstag
63 55073 4587 wichtig mim00-imap,01234567@smsmail.example.org
64 55074 4604 @waera.de
65 60601 4589 highlander mim00-highlander,michael@mellis.de
66 65150 4589 little-sunshine mim00-marleen
67 75964 4589 mail michael@mellis.de
68 75965 4589 michael@mellis.de
69 77726 4587 chat michael mim00-chat
70 93790 7662 abuse dph00-dph
71 93791 7662 postmaster dph00-dph
72 93792 7662 webmaster dph00-dph