From 9ee9e1e74bd6165778e0efe11ea7b80e19e17036 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 29 Jul 2024 09:15:15 +0200 Subject: [PATCH 01/14] import unixusers --- .../HsUnixUserHostingAssetValidator.java | 10 +- .../hs/migration/ImportHostingAssets.java | 114 +++++++++++++++--- .../resources/migration/hosting/unixuser.csv | 19 +++ 3 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 src/test/resources/migration/hosting/unixuser.csv diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index 7bcbb028..5b879c46 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -7,6 +7,7 @@ import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; import java.util.regex.Pattern; +import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty; @@ -21,6 +22,9 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { HsHostingAssetType.UNIX_USER, AlarmContact.isOptional(), + booleanProperty("locked").withDefault(false), + integerProperty("userid").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeUserId), + integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(), integerProperty("SSD soft quota").unit("GB").maxFrom("SSD hard quota").optional(), integerProperty("HDD hard quota").unit("GB").maxFrom("HDD").optional(), @@ -31,7 +35,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir), stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(), passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly()); - // TODO.spec: public SSH keys? + // TODO.spec: public SSH keys? (only if hsadmin-ng is only accessible with 2FA) } @Override @@ -46,4 +50,8 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { return "/home/pacs/" + webspaceName + "/users/" + entity.getIdentifier().substring(webspaceName.length()+DASH_LENGTH); } + + private static Integer computeUserId(PropertiesProvider propertiesProvider) { + return 0; // FIXME: from a specific numeric rage and unique per hive? + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index cda4c482..3d9bf71a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -29,12 +29,14 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import static java.util.Arrays.stream; +import static java.util.Map.entry; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toMap; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; @@ -91,6 +93,7 @@ public class ImportHostingAssets extends ImportOfficeData { static final Integer IP_NUMBER_ID_OFFSET = 1000000; static final Integer HIVE_ID_OFFSET = 2000000; static final Integer PACKET_ID_OFFSET = 3000000; + static final Integer UNIXUSER_ID_OFFSET = 4000000; record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference serverRef) {} @@ -261,6 +264,45 @@ public class ImportHostingAssets extends ImportOfficeData { """); } + @Test + @Order(14010) + void importUnixUsers() { + try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/unixuser.csv")) { + final var lines = readAllLines(reader); + importUnixUsers(justHeader(lines), withoutHeader(lines)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + @Order(14019) + void verifyUnixUsers() { + assumeThatWeAreImportingControlledTestData(); + + // no contacts yet => mostly null values + assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" + { + 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00", "locked": false, "shell": "/bin/bash", "userid": 102090}), + 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/deaf", "locked": false, "shell": "/bin/bash", "userid": 102091}), + 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/marl", "locked": false, "shell": "/bin/bash", "userid": 102093}), + 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/marl.a", "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), + 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/marl.b", "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), + 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/test", "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), + 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/mim00", "locked": false, "shell": "/bin/bash", "userid": 102147}), + 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 256, "SDD soft quota": 128, "homedir": "/home/pacs/mim00/users/1981", "locked": false, "shell": "/bin/bash", "userid": 102148}), + 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/mim00/users/mail", "locked": false, "shell": "/bin/bash", "userid": 102160}), + 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/hsh00/users/mi", "locked": false, "shell": "/bin/false", "userid": 10003}), + 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/hsh00", "locked": false, "shell": "/bin/bash", "userid": 10000}), + 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/hsh00/users/uph", "locked": false, "shell": "/bin/false", "userid": 110568}), + 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/dph00", "locked": false, "shell": "/bin/bash", "userid": 110593}), + 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/dph00/users/uph", "locked": false, "shell": "/bin/bash", "userid": 110594}) + } + """); + } + + // -------------------------------------------------------------------------------------------- + @Test @Order(11400) void validateBookingItems() { @@ -287,6 +329,8 @@ public class ImportHostingAssets extends ImportOfficeData { }); } + // -------------------------------------------------------------------------------------------- + @Test @Order(19000) @Commit @@ -323,19 +367,21 @@ public class ImportHostingAssets extends ImportOfficeData { persist(key, HsBookingItemEntityValidatorRegistry.validated(bi)); } + // ============================================================================================ + private void persistHostingAssetsOfType(final HsHostingAssetType hsHostingAssetType) { jpaAttempt.transacted(() -> { context(rbacSuperuser); hostingAssets.forEach((key, ha) -> { - if (ha.getType() == hsHostingAssetType) { - new HostingAssetEntitySaveProcessor(ha) - .preprocessEntity() - .validateEntity() - .prepareForSave() - .saveUsing(entity -> persist(key, entity)) - .validateContext(); + if (ha.getType() == hsHostingAssetType) { + new HostingAssetEntitySaveProcessor(ha) + .preprocessEntity() + .validateEntity() + .prepareForSave() + .saveUsing(entity -> persist(key, entity)) + .validateContext(); + } } - } ); }).assertSuccessful(); } @@ -402,12 +448,17 @@ public class ImportHostingAssets extends ImportOfficeData { bookingItems.put(PACKET_ID_OFFSET + packet_id, bookingItem); final var haType = determineHaType(basepacket_code); - logError(() -> assertThat(!free || haType == MANAGED_WEBSPACE || bookingItem.getRelatedProject().getDebitor().getDefaultPrefix().equals("hsh")) - .as("packet.free only supported for Hostsharing-Assets and ManagedWebspace in customer-ManagedServer, but is set for " + packet_name) + logError(() -> assertThat(!free || haType == MANAGED_WEBSPACE || bookingItem.getRelatedProject() + .getDebitor() + .getDefaultPrefix() + .equals("hsh")) + .as("packet.free only supported for Hostsharing-Assets and ManagedWebspace in customer-ManagedServer, but is set for " + + packet_name) .isTrue()); final var asset = HsHostingAssetEntity.builder() - .isLoaded(haType == MANAGED_WEBSPACE) // this turns off identifier validation to accept former default prefixes + .isLoaded(haType + == MANAGED_WEBSPACE) // this turns off identifier validation to accept former default prefixes .type(haType) .identifier(packet_name) .bookingItem(bookingItem) @@ -461,9 +512,9 @@ public class ImportHostingAssets extends ImportOfficeData { case "DAEMON" -> "Daemons"; case "MULTI" -> "Multi"; case "CPU" -> "CPU"; - case "RAM" -> returning("RAM", convert = v -> v/1024); - case "QUOTA" -> returning("SSD", convert = v -> v/1024); - case "STORAGE" -> returning("HDD", convert = v -> v/1024); + case "RAM" -> returning("RAM", convert = v -> v / 1024); + case "QUOTA" -> returning("SSD", convert = v -> v / 1024); + case "STORAGE" -> returning("HDD", convert = v -> v / 1024); case "TRAFFIC" -> "Traffic"; case "OFFICE" -> returning("Online Office Server", convert = v -> v == 1); @@ -526,7 +577,7 @@ public class ImportHostingAssets extends ImportOfficeData { case "SLAPLAT8H" -> "EXT8H"; default -> throw new IllegalArgumentException("unknown basecomponent_code: " + basecomponent_code); }; - if ( ofNullable(asset.getBookingItem().getResources().get(name)).map("BASIC"::equals).orElse(true) ) { + if (ofNullable(asset.getBookingItem().getResources().get(name)).map("BASIC"::equals).orElse(true)) { asset.getBookingItem().getResources().put(name, slaValue); } } else if (name.startsWith("SLA")) { @@ -537,7 +588,38 @@ public class ImportHostingAssets extends ImportOfficeData { }); } - V returning(final V value, final Object... assignments) { + private void importUnixUsers(final String[] header, final List records) { + final var columns = new Columns(header); + records.stream() + .map(this::trimAll) + .map(row -> new Record(columns, row)) + .forEach(rec -> { + final var unixuser_id = rec.getInteger("unixuser_id"); + final var packet_id = rec.getInteger("packet_id"); + final var unixUserAsset = HsHostingAssetEntity.builder() + .type(UNIX_USER) + .parentAsset(hostingAssets.get(PACKET_ID_OFFSET+packet_id)) + .identifier(rec.getString("name")) + .caption(rec.getString("comment")) + .config(Map.ofEntries( + entry("shell", rec.getString("shell")), + entry("homedir", rec.getString("homedir")), + entry("locked", rec.getBoolean("locked")), + entry("userid", rec.getInteger("userid")), + entry("SDD soft quota", rec.getInteger("quota_softlimit")), + entry("SDD hard quota", rec.getInteger("quota_hardlimit")), + entry("HDD soft quota", rec.getInteger("storage_softlimit")), + entry("HDD hard quota", rec.getInteger("storage_hardlimit")) + )) + .build(); + hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); + }); + } + + V returning( + final V value, + @SuppressWarnings("unused") final Object... assignments // DSL-hack: just used for side effects on caller-side + ) { return value; } diff --git a/src/test/resources/migration/hosting/unixuser.csv b/src/test/resources/migration/hosting/unixuser.csv new file mode 100644 index 00000000..ee08e2f0 --- /dev/null +++ b/src/test/resources/migration/hosting/unixuser.csv @@ -0,0 +1,19 @@ +unixuser_id;name;comment;shell;homedir;locked;packet_id;userid;quota_softlimit;quota_hardlimit;storage_softlimit;storage_hardlimit +100824;hsh00;Hostsharing Paket;/bin/bash;/home/pacs/hsh00;0;630;10000;0;0;0;0 + +5803;lug00;LUGs;/bin/bash;/home/pacs/lug00;0;1094;102090;0;0;0;0 +5805;lug00-wla.1;Paul Klemm;/bin/bash;/home/pacs/lug00/users/deaf;0;1094;102091;0;0;0;0 +5809;lug00-wla.2;Walter Müller;/bin/bash;/home/pacs/lug00/users/marl;0;1094;102093;0;0;0;0 +5811;lug00-ola.a;LUG OLA - POP a;/usr/bin/passwd;/home/pacs/lug00/users/marl.a;1;1094;102094;0;0;0;0 +5813;lug00-ola.b;LUG OLA - POP b;/usr/bin/passwd;/home/pacs/lug00/users/marl.b;1;1094;102095;0;0;0;0 +5835;lug00-test;Test;/usr/bin/passwd;/home/pacs/lug00/users/test;0;1094;102106;0;0;0;0 + +100705;hsh00-mim;Michael Mellis;/bin/false;/home/pacs/hsh00/users/mi;0;630;10003;0;0;0;0 +5964;mim00;Michael Mellis;/bin/bash;/home/pacs/mim00;0;1112;102147;0;0;0;0 +5966;mim00-1981;Jahrgangstreffen 1981;/bin/bash;/home/pacs/mim00/users/1981;0;1112;102148;128;256;0;0 +5990;mim00-mail;Mailbox;/bin/bash;/home/pacs/mim00/users/mail;0;1112;102160;0;0;0;0 + +167846;hsh00-dph;hsh00-uph;/bin/false;/home/pacs/hsh00/users/uph;0;630;110568;0;0;0;0 +169546;dph00;Reinhard Wiese;/bin/bash;/home/pacs/dph00;0;19959;110593;0;0;0;0 +169596;dph00-uph;Domain admin;/bin/bash;/home/pacs/dph00/users/uph;0;19959;110594;0;0;0;0 + -- 2.39.5 From 20f154c145f3d838b664ad39a64c1fbeb890fdc8 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 29 Jul 2024 14:38:50 +0200 Subject: [PATCH 02/14] import emailaliases --- ...HsManagedWebspaceBookingItemValidator.java | 16 +-- .../HsEMailAddressHostingAssetValidator.java | 2 +- .../HsEMailAliasHostingAssetValidator.java | 8 +- .../HsUnixUserHostingAssetValidator.java | 12 +-- .../hs/validation/IntegerProperty.java | 24 +++-- .../hs/validation/ValidatableProperty.java | 8 +- .../hsadminng/mapper/PatchableMapWrapper.java | 10 +- .../hsadminng/hs/migration/CsvDataImport.java | 10 ++ .../hs/migration/ImportHostingAssets.java | 100 ++++++++++++++---- .../migration/hosting/emailalias.csv | 7 ++ 10 files changed, 144 insertions(+), 53 deletions(-) create mode 100644 src/test/resources/migration/hosting/emailalias.csv diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java index ffa2b525..4b02d4d3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java @@ -38,8 +38,8 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator ); } - private static TriFunction> unixUsers() { - return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { + private static TriFunction, Integer, List> unixUsers() { + return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = ofNullable(entity.getRelatedHostingAsset()) .map(ha -> ha.getSubHostingAssets().stream() .filter(subAsset -> subAsset.getType() == UNIX_USER) @@ -53,8 +53,8 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator }; } - private static TriFunction> databaseUsers() { - return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { + private static TriFunction, Integer, List> databaseUsers() { + return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var dbUserCount = ofNullable(entity.getRelatedHostingAsset()) .map(ha -> ha.getSubHostingAssets().stream() .filter(bi -> bi.getType() == PGSQL_USER || bi.getType() == MARIADB_USER ) @@ -68,8 +68,8 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator }; } - private static TriFunction> databases() { - return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { + private static TriFunction, Integer, List> databases() { + return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = ofNullable(entity.getRelatedHostingAsset()) .map(ha -> ha.getSubHostingAssets().stream() .filter(bi -> bi.getType()==PGSQL_USER || bi.getType()==MARIADB_USER ) @@ -85,8 +85,8 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator }; } - private static TriFunction> eMailAddresses() { - return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { + private static TriFunction, Integer, List> eMailAddresses() { + return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = ofNullable(entity.getRelatedHostingAsset()) .map(ha -> ha.getSubHostingAssets().stream() .filter(bi -> bi.getType() == DOMAIN_MBOX_SETUP) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java index d77451e7..eea872fe 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java @@ -11,7 +11,7 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope 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 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 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 + "$"; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java index d9bcb01a..720375da 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java @@ -10,8 +10,10 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope class HsEMailAliasHostingAssetValidator 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 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 EMAIL_ADDRESS_REGEX = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"; // RFC 5322 + private static final String INCLUDE_REGEX = "^:include:/.*$"; + private static final String PIPE_REGEX = "^|.*$"; public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322 HsEMailAliasHostingAssetValidator() { @@ -19,13 +21,13 @@ class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator { AlarmContact.isOptional(), arrayOf( - stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_REGEX) + stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_REGEX, INCLUDE_REGEX, PIPE_REGEX) ).required().minLength(1)); } @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9]+$"); + return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9][a-z0-9\\.-]*$"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index 5b879c46..a49347e4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -22,13 +22,13 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { HsHostingAssetType.UNIX_USER, AlarmContact.isOptional(), - booleanProperty("locked").withDefault(false), + booleanProperty("locked").readOnly(), integerProperty("userid").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeUserId), - integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(), - integerProperty("SSD soft quota").unit("GB").maxFrom("SSD hard quota").optional(), - integerProperty("HDD hard quota").unit("GB").maxFrom("HDD").optional(), - integerProperty("HDD soft quota").unit("GB").maxFrom("HDD hard quota").optional(), + integerProperty("SSD hard quota").unit("MB").maxFrom("SSD").withFactor(1024).optional(), + integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(), + integerProperty("HDD hard quota").unit("MB").maxFrom("HDD").withFactor(1024).optional(), + integerProperty("HDD soft quota").unit("MB").maxFrom("HDD hard quota").optional(), enumerationProperty("shell") .values("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd") .withDefault("/bin/false"), @@ -41,7 +41,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9]+$"); + return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\.-]+$"); } private static String computeHomedir(final PropertiesProvider propertiesProvider) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java index 7021f9e1..f61f0d7d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java @@ -7,7 +7,7 @@ import org.apache.commons.lang3.Validate; import java.util.List; @Setter -public class IntegerProperty extends ValidatableProperty { +public class IntegerProperty

> extends ValidatableProperty { private final static String[] KEY_ORDER = Array.join( ValidatableProperty.KEY_ORDER_HEAD, @@ -19,10 +19,11 @@ public class IntegerProperty extends ValidatableProperty integerProperty(final String propertyName) { + return new IntegerProperty<>(propertyName); } private IntegerProperty(final String propertyName) { @@ -35,14 +36,19 @@ public class IntegerProperty extends ValidatableProperty[], T[]> if (asTotalLimitValidators == null) { asTotalLimitValidators = new ArrayList<>(); } - final TriFunction> validator = - (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { + final TriFunction, Integer, List> validator = + (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var total = entity.getSubBookingItems().stream() .map(server -> server.getResources().get(propertyName)) @@ -169,11 +169,11 @@ protected void setDeferredInit(final Function[], T[]> return thresholdPercentage; } - public ValidatableProperty eachComprising(final int factor, final TriFunction> validator) { + public ValidatableProperty eachComprising(final int factor, final TriFunction, Integer, List> validator) { if (asTotalLimitValidators == null) { asTotalLimitValidators = new ArrayList<>(); } - asTotalLimitValidators.add((final HsBookingItemEntity entity) -> validator.apply(entity, (IntegerProperty)this, factor)); + asTotalLimitValidators.add((final HsBookingItemEntity entity) -> validator.apply(entity, (IntegerProperty)this, factor)); return this; } diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/PatchableMapWrapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/PatchableMapWrapper.java index 21153b14..ffd9c1bd 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/PatchableMapWrapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/PatchableMapWrapper.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.mapper; import org.apache.commons.lang3.tuple.ImmutablePair; import jakarta.validation.constraints.NotNull; +import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Set; @@ -56,16 +57,19 @@ public class PatchableMapWrapper implements Map { return "{\n" + ( keySet().stream().sorted() - .map(k -> " \"" + k + "\": " + optionallyQuoted(get(k)))) + .map(k -> " \"" + k + "\": " + formatted(get(k)))) .collect(joining(",\n") ) + "\n}\n"; } - private Object optionallyQuoted(final Object value) { - if ( value instanceof Number || value instanceof Boolean ) { + private Object formatted(final Object value) { + if ( value == null || value instanceof Number || value instanceof Boolean ) { return value; } + if ( value.getClass().isArray() ) { + return "\"" + Arrays.toString( (Object[]) value) + "\""; + } return "\"" + value + "\""; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java index de741b46..9061ce9a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.migration; import com.opencsv.CSVParserBuilder; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; +import lombok.SneakyThrows; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; @@ -37,6 +38,7 @@ import static java.lang.Boolean.parseBoolean; import static java.util.Arrays.stream; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; +import static net.hostsharing.hsadminng.mapper.Array.emptyArray; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; @@ -113,6 +115,14 @@ public class CsvDataImport extends ContextBasedTest { return records.subList(1, records.size()); } + @SneakyThrows + public static String[] parseCsvLine(final String csvLine) { + try (final var reader = new CSVReader(new StringReader(csvLine))) { + return ofNullable(reader.readNext()).orElse(emptyArray(String.class)); + } + } + + String[] trimAll(final String[] record) { for (int i = 0; i < record.length; ++i) { if (record[i] != null) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 3d9bf71a..23e6829d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -23,8 +23,10 @@ import org.springframework.test.annotation.Commit; import org.springframework.test.annotation.DirtiesContext; import java.io.Reader; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -33,6 +35,7 @@ import static java.util.Map.entry; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toMap; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.EMAIL_ALIAS; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; @@ -283,20 +286,49 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null values assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { - 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00", "locked": false, "shell": "/bin/bash", "userid": 102090}), - 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/deaf", "locked": false, "shell": "/bin/bash", "userid": 102091}), - 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/marl", "locked": false, "shell": "/bin/bash", "userid": 102093}), - 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/marl.a", "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), - 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/marl.b", "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), - 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/lug00/users/test", "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), - 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/mim00", "locked": false, "shell": "/bin/bash", "userid": 102147}), - 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 256, "SDD soft quota": 128, "homedir": "/home/pacs/mim00/users/1981", "locked": false, "shell": "/bin/bash", "userid": 102148}), - 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/mim00/users/mail", "locked": false, "shell": "/bin/bash", "userid": 102160}), - 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/hsh00/users/mi", "locked": false, "shell": "/bin/false", "userid": 10003}), - 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/hsh00", "locked": false, "shell": "/bin/bash", "userid": 10000}), - 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/hsh00/users/uph", "locked": false, "shell": "/bin/false", "userid": 110568}), - 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/dph00", "locked": false, "shell": "/bin/bash", "userid": 110593}), - 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SDD hard quota": 0, "SDD soft quota": 0, "homedir": "/home/pacs/dph00/users/uph", "locked": false, "shell": "/bin/bash", "userid": 110594}) + 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), + 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}), + 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102093}), + 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), + 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), + 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), + 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}), + 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}), + 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}), + 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 10003}), + 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}), + 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}), + 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), + 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) + } + """); + } + + @Test + @Order(14020) + void importEmailAliases() { + try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/emailalias.csv")) { + final var lines = readAllLines(reader); + importEmailAliases(justHeader(lines), withoutHeader(lines)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + @Order(14029) + void verifyEmailAliases() { + assumeThatWeAreImportingControlledTestData(); + + // no contacts yet => mostly null values + assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace(""" + { + 4002403=HsHostingAssetEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), + 4002405=HsHostingAssetEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}), + 4002429=HsHostingAssetEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}), + 4002431=HsHostingAssetEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}), + 4002449=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}), + 4002451=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}) } """); } @@ -352,6 +384,8 @@ public class ImportHostingAssets extends ImportOfficeData { persistHostingAssetsOfType(MANAGED_SERVER); persistHostingAssetsOfType(MANAGED_WEBSPACE); persistHostingAssetsOfType(IPV4_NUMBER); + persistHostingAssetsOfType(UNIX_USER); + persistHostingAssetsOfType(EMAIL_ALIAS); } @Test @@ -601,21 +635,45 @@ public class ImportHostingAssets extends ImportOfficeData { .parentAsset(hostingAssets.get(PACKET_ID_OFFSET+packet_id)) .identifier(rec.getString("name")) .caption(rec.getString("comment")) - .config(Map.ofEntries( + .config(new HashMap<>(Map.ofEntries( entry("shell", rec.getString("shell")), - entry("homedir", rec.getString("homedir")), + // entry("homedir", rec.getString("homedir")), do not import, it's calculated entry("locked", rec.getBoolean("locked")), entry("userid", rec.getInteger("userid")), - entry("SDD soft quota", rec.getInteger("quota_softlimit")), - entry("SDD hard quota", rec.getInteger("quota_hardlimit")), + entry("SSD soft quota", rec.getInteger("quota_softlimit")), + entry("SSD hard quota", rec.getInteger("quota_hardlimit")), entry("HDD soft quota", rec.getInteger("storage_softlimit")), entry("HDD hard quota", rec.getInteger("storage_hardlimit")) + ))) + .build(); + hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); + }); + } + + private void importEmailAliases(final String[] header, final List records) { + final var columns = new Columns(header); + records.stream() + .map(this::trimAll) + .map(row -> new Record(columns, row)) + .forEach(rec -> { + final var unixuser_id = rec.getInteger("emailalias_id"); + final var packet_id = rec.getInteger("pac_id"); + final var targets = parseCsvLine(rec.getString("target")); + final var unixUserAsset = HsHostingAssetEntity.builder() + .type(EMAIL_ALIAS) + .parentAsset(hostingAssets.get(PACKET_ID_OFFSET+packet_id)) + .identifier(rec.getString("name")) + .caption(rec.getString("name")) + .config(Map.ofEntries( + entry("target", targets) )) .build(); hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); }); } + // ============================================================================================ + V returning( final V value, @SuppressWarnings("unused") final Object... assignments // DSL-hack: just used for side effects on caller-side @@ -664,7 +722,11 @@ public class ImportHostingAssets extends ImportOfficeData { .filter(hae -> hae.getValue().getType() == t) .limit(maxCount) ) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue))); + .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, ImportHostingAssets::uniqueKeys, TreeMap::new))); + } + + protected static V uniqueKeys(final V v1, final V v2) { + throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2)); } private String firstOfEachType( diff --git a/src/test/resources/migration/hosting/emailalias.csv b/src/test/resources/migration/hosting/emailalias.csv new file mode 100644 index 00000000..7701c61a --- /dev/null +++ b/src/test/resources/migration/hosting/emailalias.csv @@ -0,0 +1,7 @@ +emailalias_id;pac_id;name;target +2403;1094;lug00;michael.mellis@example.com +2405;1094;lug00-wla-listar;|/home/pacs/lug00/users/in/mailinglist/listar +2429;1112;mim00;mim12-mi@mim12.hostsharing.net +2431;1112;mim00-abruf;michael.mellis@hostsharing.net +2449;1112;mim00-hhfx;"mim00-hhfx,""|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l""" +2451;1112;mim00-hhfx-l;:include:/home/pacs/mim00/etc/hhfx.list -- 2.39.5 From 99a93586796acc28b97a664ed7a9e97a3620234e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 29 Jul 2024 15:40:58 +0200 Subject: [PATCH 03/14] introduce EntityManager to HostingAssetEntitySaveProcessor --- .../hs/hosting/asset/HsHostingAssetController.java | 6 +++--- .../validators/HostingAssetEntitySaveProcessor.java | 9 ++++++--- .../hsadminng/hs/validation/HsEntityValidator.java | 9 +++++---- .../hsadminng/hs/validation/ValidatableProperty.java | 3 ++- .../HsMariaDbUserHostingAssetValidatorUnitTest.java | 5 ++++- .../HsPostgreSqlUserHostingAssetValidatorUnitTest.java | 5 ++++- .../HsUnixUserHostingAssetValidatorUnitTest.java | 7 +++++-- .../hsadminng/hs/migration/ImportHostingAssets.java | 4 ++-- .../hs/validation/PasswordPropertyUnitTest.java | 4 +++- 9 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java index 3ca7efff..66402aac 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java @@ -72,7 +72,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { final var entity = mapper.map(body, HsHostingAssetEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); - final var mapped = new HostingAssetEntitySaveProcessor(entity) + final var mapped = new HostingAssetEntitySaveProcessor(em, entity) .preprocessEntity() .validateEntity() .prepareForSave() @@ -133,7 +133,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { new HsHostingAssetEntityPatcher(em, entity).apply(body); - final var mapped = new HostingAssetEntitySaveProcessor(entity) + final var mapped = new HostingAssetEntitySaveProcessor(em, entity) .preprocessEntity() .validateEntity() .prepareForSave() @@ -162,5 +162,5 @@ public class HsHostingAssetController implements HsHostingAssetsApi { @SuppressWarnings("unchecked") final BiConsumer ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> resource.setConfig(HostingAssetEntityValidatorRegistry.forType(entity.getType()) - .revampProperties(entity, (Map) resource.getConfig())); + .revampProperties(em, entity, (Map) resource.getConfig())); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java index 189b3314..495ea665 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import jakarta.persistence.EntityManager; import java.util.Map; import java.util.function.Function; @@ -15,10 +16,12 @@ public class HostingAssetEntitySaveProcessor { private final HsEntityValidator validator; private String expectedStep = "preprocessEntity"; + private final EntityManager em; private HsHostingAssetEntity entity; private HsHostingAssetResource resource; - public HostingAssetEntitySaveProcessor(final HsHostingAssetEntity entity) { + public HostingAssetEntitySaveProcessor(final EntityManager em, final HsHostingAssetEntity entity) { + this.em = em; this.entity = entity; this.validator = HostingAssetEntityValidatorRegistry.forType(entity.getType()); } @@ -41,7 +44,7 @@ public class HostingAssetEntitySaveProcessor { @SuppressWarnings("unchecked") public HostingAssetEntitySaveProcessor prepareForSave() { step("prepareForSave", "saveUsing"); - validator.prepareProperties(entity); + validator.prepareProperties(em, entity); return this; } @@ -70,7 +73,7 @@ public class HostingAssetEntitySaveProcessor { @SuppressWarnings("unchecked") public HsHostingAssetResource revampProperties() { step("revampProperties", null); - final var revampedProps = validator.revampProperties(entity, (Map) resource.getConfig()); + final var revampedProps = validator.revampProperties(em, entity, (Map) resource.getConfig()); resource.setConfig(revampedProps); return resource; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java index fac624cf..f6d72789 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.validation; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -106,21 +107,21 @@ public abstract class HsEntityValidator { throw new IllegalArgumentException("Integer value (or null) expected, but got " + value); } - public void prepareProperties(final E entity) { + public void prepareProperties(final EntityManager em, final E entity) { stream(propertyValidators).forEach(p -> { if ( p.isWriteOnly() && p.isComputed()) { - entity.directProps().put(p.propertyName, p.compute(entity)); + entity.directProps().put(p.propertyName, p.compute(em, entity)); } }); } - public Map revampProperties(final E entity, final Map config) { + public Map revampProperties(final EntityManager em, final E entity, final Map config) { final var copy = new HashMap<>(config); stream(propertyValidators).forEach(p -> { if (p.isWriteOnly()) { copy.remove(p.propertyName); } else if (p.isReadOnly() && p.isComputed()) { - copy.put(p.propertyName, p.compute(entity)); + copy.put(p.propertyName, p.compute(em, entity)); } }); return copy; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java index d98ff34c..d5678e12 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java @@ -9,6 +9,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.mapper.Array; import org.apache.commons.lang3.function.TriFunction; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -307,7 +308,7 @@ protected void setDeferredInit(final Function[], T[]> return self(); } - public T compute(final E entity) { + public T compute(final EntityManager em, final E entity) { return computedBy.apply(entity); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java index d5f4948e..259d980e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; import org.junit.jupiter.api.Test; +import jakarta.persistence.EntityManager; import java.util.HashMap; import java.util.stream.Stream; @@ -24,6 +25,8 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { .caption("some valid test MariaDB-Instance") .build(); + private EntityManager em = null; // not actually needed in these test cases + private static HsHostingAssetEntityBuilder givenValidMariaDbUserBuilder() { return HsHostingAssetEntity.builder() .type(MARIADB_USER) @@ -58,7 +61,7 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { // when // HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); // not needed for mysql_native_password - validator.prepareProperties(givenMariaDbUserHostingAsset); + validator.prepareProperties(em, givenMariaDbUserHostingAsset); // then assertThat(givenMariaDbUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java index 0875ea7b..4b48dc5e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; import org.junit.jupiter.api.Test; +import jakarta.persistence.EntityManager; import java.nio.charset.Charset; import java.util.Base64; import java.util.HashMap; @@ -27,6 +28,8 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest { .caption("some valid test PgSql-Instance") .build(); + private EntityManager em = null; // not actually needed in these test cases + private static HsHostingAssetEntityBuilder givenValidMariaDbUserBuilder() { return HsHostingAssetEntity.builder() .type(PGSQL_USER) @@ -61,7 +64,7 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest { // when HashGenerator.nextSalt(new String(Base64.getDecoder().decode("L1QxSVNyTU81b3NZS1djNg=="), Charset.forName("latin1"))); - validator.prepareProperties(givenMariaDbUserHostingAsset); + validator.prepareProperties(em, givenMariaDbUserHostingAsset); // then assertThat(givenMariaDbUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java index 0d128cab..7a27db88 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import org.junit.jupiter.api.Test; +import jakarta.persistence.EntityManager; import java.util.HashMap; import java.util.stream.Stream; @@ -43,6 +44,8 @@ class HsUnixUserHostingAssetValidatorUnitTest { ))) .build(); + private EntityManager em = null; // not actually needed in these test cases + @Test void preparesUnixUser() { // given @@ -51,7 +54,7 @@ class HsUnixUserHostingAssetValidatorUnitTest { // when HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); - validator.prepareProperties(unixUserHostingAsset); + validator.prepareProperties(em, unixUserHostingAsset); // then assertThat(unixUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries( @@ -142,7 +145,7 @@ class HsUnixUserHostingAssetValidatorUnitTest { // when HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); - final var result = validator.revampProperties(unixUserHostingAsset, unixUserHostingAsset.getConfig()); + final var result = validator.revampProperties(em, unixUserHostingAsset, unixUserHostingAsset.getConfig()); // then assertThat(result).containsExactlyInAnyOrderEntriesOf(ofEntries( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 23e6829d..b9cfac9f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -352,7 +352,7 @@ public class ImportHostingAssets extends ImportOfficeData { void validateHostingAssets() { hostingAssets.forEach((id, ha) -> { try { - new HostingAssetEntitySaveProcessor(ha) + new HostingAssetEntitySaveProcessor(em, ha) .preprocessEntity() .validateEntity(); } catch (final Exception exc) { @@ -408,7 +408,7 @@ public class ImportHostingAssets extends ImportOfficeData { context(rbacSuperuser); hostingAssets.forEach((key, ha) -> { if (ha.getType() == hsHostingAssetType) { - new HostingAssetEntitySaveProcessor(ha) + new HostingAssetEntitySaveProcessor(em, ha) .preprocessEntity() .validateEntity() .prepareForSave() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java index 663a7715..47e40336 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -19,6 +20,7 @@ class PasswordPropertyUnitTest { private final ValidatableProperty passwordProp = passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LINUX_SHA512).writeOnly(); private final List violations = new ArrayList<>(); + private EntityManager em = null; // not actually needed in these test cases @ParameterizedTest @ValueSource(strings = { @@ -99,7 +101,7 @@ class PasswordPropertyUnitTest { void shouldComputeHash() { // when - final var result = passwordProp.compute(new PropertiesProvider() { + final var result = passwordProp.compute(em, new PropertiesProvider() { @Override public Map directProps() { -- 2.39.5 From 5d438f214f8750f0e8b8a5f5445ed039566009c4 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 29 Jul 2024 19:15:34 +0200 Subject: [PATCH 04/14] generate userid --- .../HsUnixUserHostingAssetValidator.java | 9 +++--- .../hs/validation/HsEntityValidator.java | 2 +- .../hs/validation/PasswordProperty.java | 2 +- .../hs/validation/ValidatableProperty.java | 12 ++++---- .../7010-hs-hosting-asset.sql | 15 ++++++++++ .../hs/migration/ImportHostingAssets.java | 29 +++++++++++++++++++ 6 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index a49347e4..72c746d2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; +import jakarta.persistence.EntityManager; import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; @@ -23,7 +24,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { AlarmContact.isOptional(), booleanProperty("locked").readOnly(), - integerProperty("userid").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeUserId), + integerProperty("userid").computedBy(HsUnixUserHostingAssetValidator::computeUserId), integerProperty("SSD hard quota").unit("MB").maxFrom("SSD").withFactor(1024).optional(), integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(), @@ -44,14 +45,14 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\.-]+$"); } - private static String computeHomedir(final PropertiesProvider propertiesProvider) { + private static String computeHomedir(final EntityManager em, final PropertiesProvider propertiesProvider) { final var entity = (HsHostingAssetEntity) propertiesProvider; final var webspaceName = entity.getParentAsset().getIdentifier(); return "/home/pacs/" + webspaceName + "/users/" + entity.getIdentifier().substring(webspaceName.length()+DASH_LENGTH); } - private static Integer computeUserId(PropertiesProvider propertiesProvider) { - return 0; // FIXME: from a specific numeric rage and unique per hive? + private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) { + return Math.toIntExact((Long) em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')").getSingleResult()); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java index f6d72789..741b9d59 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -109,7 +109,7 @@ public abstract class HsEntityValidator { public void prepareProperties(final EntityManager em, final E entity) { stream(propertyValidators).forEach(p -> { - if ( p.isWriteOnly() && p.isComputed()) { + if (!p.isReadOnly() && p.isComputed()) { entity.directProps().put(p.propertyName, p.compute(em, entity)); } }); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java index 732151cd..ac88e7a6 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java @@ -34,7 +34,7 @@ public class PasswordProperty extends StringProperty { public PasswordProperty hashedUsing(final Algorithm algorithm) { this.hashedUsing = algorithm; - computedBy((entity) + computedBy((em, entity) -> ofNullable(entity.getDirectValue(propertyName, String.class)) .map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password)) .orElse(null)); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java index d5678e12..429f97d5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import static java.lang.Boolean.FALSE; @@ -48,7 +49,7 @@ public abstract class ValidatableProperty

, T private T defaultValue; @JsonIgnore - private Function computedBy; + private BiFunction computedBy; @Accessors(makeFinal = true, chain = true, fluent = false) private boolean computed; // used in descriptor, because computedBy cannot be rendered to a text string @@ -96,7 +97,6 @@ protected void setDeferredInit(final Function[], T[]> public P readOnly() { this.readOnly = true; - optional(); return self(); } @@ -236,8 +236,8 @@ protected void setDeferredInit(final Function[], T[]> protected abstract void validate(final List result, final T propValue, final PropertiesProvider propProvider); public void verifyConsistency(final Map.Entry, ?> typeDef) { - if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null) { - throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" ); + if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && !computed) { + throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .computed(...), .readOnly(), .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" ); } } @@ -302,14 +302,14 @@ protected void setDeferredInit(final Function[], T[]> .toList(); } - public P computedBy(final Function compute) { + public P computedBy(final BiFunction compute) { this.computedBy = compute; this.computed = true; return self(); } public T compute(final EntityManager em, final E entity) { - return computedBy.apply(entity); + return computedBy.apply(em, entity); } @Override diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index 3b1b54d1..48a79a71 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -111,6 +111,21 @@ create trigger hs_hosting_asset_type_hierarchy_check_tg --// + +-- ============================================================================ +--changeset hosting-asset-system-sequences:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +CREATE SEQUENCE IF NOT EXISTS hs_hosting_asset_unixuser_system_id_seq + AS integer + MINVALUE 100000000 + MAXVALUE 199999999 + NO CYCLE + OWNED BY NONE; + +--// + + -- ============================================================================ --changeset hosting-asset-BOOKING-ITEM-HIERARCHY-CHECK:1 endDelimiter:--// -- ---------------------------------------------------------------------------- diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index b9cfac9f..8f2614c9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -388,6 +388,35 @@ public class ImportHostingAssets extends ImportOfficeData { persistHostingAssetsOfType(EMAIL_ALIAS); } + @Test + @Order(19010) + void verifyPersistedUnixUsersWithUserId() { + assumeThatWeAreImportingControlledTestData(); + + // no contacts yet => mostly null values + assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" + { + 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000000}), + 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000001}), + 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000002}), + 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000003}), + 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000004}), + 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000005}), + 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000006}), + 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000007}), + 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000008}), + 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 100000009}), + 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000010}), + 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 100000011}), + 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000012}), + 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000013}) + } + """); + } + + + // ============================================================================================ + @Test @Order(99999) void logErrors() { -- 2.39.5 From 7193772f98000eeb75ca0bcb3e4333b6fd56ba35 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 30 Jul 2024 10:02:01 +0200 Subject: [PATCH 05/14] allow _ in unixuser names etc. --- .../HsDomainDnsSetupHostingAssetValidator.java | 2 +- .../HsEMailAddressHostingAssetValidator.java | 2 +- .../HsEMailAliasHostingAssetValidator.java | 4 ++-- .../HsManagedWebspaceHostingAssetValidator.java | 2 +- .../validators/HsUnixUserHostingAssetValidator.java | 10 ++++++---- ...sDomainDnsSetupHostingAssetValidatorUnitTest.java | 6 +++--- .../HsEMailAddressHostingAssetValidatorUnitTest.java | 4 ++-- .../HsEMailAliasHostingAssetValidatorUnitTest.java | 2 +- .../hsadminng/hs/migration/CsvDataImport.java | 10 +++++----- .../hsadminng/hs/migration/ImportHostingAssets.java | 12 +++++++----- 10 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index 97c44ce2..b24efe3b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -18,7 +18,7 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator { // according to RFC 1035 (section 5) and RFC 1034 - static final String RR_REGEX_NAME = "([a-z0-9\\.-]+|@)\\s+"; + static final String RR_REGEX_NAME = "([a-z0-9\\._-]+|@)\\s+"; static final String RR_REGEX_TTL = "(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*"; static final String RR_REGEX_IN = "IN\\s+"; // record class IN for Internet static final String RR_RECORD_TYPE = "[A-Z]+\\s+"; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java index eea872fe..cfaa5eda 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java @@ -11,7 +11,7 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope 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 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 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 + "$"; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java index 720375da..5fdb2fb3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java @@ -10,7 +10,7 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope class HsEMailAliasHostingAssetValidator 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 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 EMAIL_ADDRESS_REGEX = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"; // RFC 5322 private static final String INCLUDE_REGEX = "^:include:/.*$"; private static final String PIPE_REGEX = "^|.*$"; @@ -28,6 +28,6 @@ class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator { @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9][a-z0-9\\.-]*$"); + return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9][a-z0-9\\._-]*$"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index b56f8549..b962e655 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -11,7 +11,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator super( MANAGED_WEBSPACE, AlarmContact.isOptional(), - NO_EXTRA_PROPERTIES); + NO_EXTRA_PROPERTIES); // TODO.impl: groupid missing, should be equal to main user } @Override diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index 72c746d2..0d913879 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -30,8 +30,8 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(), integerProperty("HDD hard quota").unit("MB").maxFrom("HDD").withFactor(1024).optional(), integerProperty("HDD soft quota").unit("MB").maxFrom("HDD hard quota").optional(), - enumerationProperty("shell") - .values("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd") + stringProperty("shell") + .provided("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd") .withDefault("/bin/false"), stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir), stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(), @@ -42,7 +42,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\.-]+$"); + return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\._-]+$"); } private static String computeHomedir(final EntityManager em, final PropertiesProvider propertiesProvider) { @@ -53,6 +53,8 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { } private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) { - return Math.toIntExact((Long) em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')").getSingleResult()); + final Object result = em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')", Integer.class) + .getSingleResult(); + return (Integer) result; } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index 7f66379c..cfe99ae7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -68,7 +68,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { "{type=boolean, propertyName=auto-WILDCARD-AAAA-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-DKIM-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-SPF-RR, defaultValue=true}", - "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*], required=true}}" + "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[([a-z0-9\\._-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\._-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*], required=true}}" ); } @@ -166,8 +166,8 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( "'DOMAIN_DNS_SETUP:example.org|DNS.config.TTL' is expected to be of type Integer, but is of type String", - "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", - "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); + "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\._-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\._-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", + "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\._-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\._-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidatorUnitTest.java index f606f209..4a30f394 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidatorUnitTest.java @@ -39,7 +39,7 @@ class HsEMailAddressHostingAssetValidatorUnitTest { 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}"); + "{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.-]+$], maxLength=320}, required=true, minLength=1}"); } @Test @@ -73,7 +73,7 @@ class HsEMailAddressHostingAssetValidatorUnitTest { 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"); + "'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-z0-9\\._-]*)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$] but 'garbage' does not match any"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java index a992d858..19e50d24 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java @@ -22,7 +22,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( - "{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}"); + "{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.-]+$, ^:include:/.*$, ^|.*$], maxLength=320}, required=true, minLength=1}"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java index 9061ce9a..d6ee39d0 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -70,7 +70,7 @@ public class CsvDataImport extends ContextBasedTest { @MockBean HttpServletRequest request; - private static final List errors = new ArrayList<>(); + static final List errors = new ArrayList<>(); public List readAllLines(Reader reader) throws Exception { @@ -136,7 +136,7 @@ public class CsvDataImport extends ContextBasedTest { try { final var asString = entity.toString(); if ( asString.contains("'null null, null'") || asString.equals("person()")) { - System.err.println("skipping to persist empty record-id " + id + " #" + entity.hashCode() + ": " + entity); + errors.add("skipping to persist empty record-id " + id + " #" + entity.hashCode() + ": " + entity); return entity; } //System.out.println("persisting #" + entity.hashCode() + ": " + entity); @@ -145,8 +145,8 @@ public class CsvDataImport extends ContextBasedTest { // em.flush(); // makes it slow, but produces better error messages // System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); } catch (Exception exc) { - System.err.println("failed to persist #" + entity.hashCode() + ": " + entity); - System.err.println(exc); + errors.add("failed to persist #" + entity.hashCode() + ": " + entity); + errors.add(exc.toString()); } return entity; } @@ -225,7 +225,7 @@ public class CsvDataImport extends ContextBasedTest { try { assertion.run(); } catch (final AssertionError exc) { - errors.add(exc); + errors.add(exc.toString()); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 8f2614c9..beeb3391 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -97,6 +97,7 @@ public class ImportHostingAssets extends ImportOfficeData { static final Integer HIVE_ID_OFFSET = 2000000; static final Integer PACKET_ID_OFFSET = 3000000; static final Integer UNIXUSER_ID_OFFSET = 4000000; + static final Integer EMAILALIAS_ID_OFFSET = 5000000; record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference serverRef) {} @@ -342,7 +343,7 @@ public class ImportHostingAssets extends ImportOfficeData { try { HsBookingItemEntityValidatorRegistry.validated(bi); } catch (final Exception exc) { - System.err.println("validation failed for id:" + id + "( " + bi + "): " + exc.getMessage()); + errors.add("validation failed for id:" + id + "( " + bi + "): " + exc.getMessage()); } }); } @@ -356,7 +357,7 @@ public class ImportHostingAssets extends ImportOfficeData { .preprocessEntity() .validateEntity(); } catch (final Exception exc) { - System.err.println("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage()); + errors.add("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage()); } }); } @@ -388,12 +389,14 @@ public class ImportHostingAssets extends ImportOfficeData { persistHostingAssetsOfType(EMAIL_ALIAS); } + @Test @Order(19010) void verifyPersistedUnixUsersWithUserId() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null values + // no contacts yet => mostly null value + // FIXME: keep original userids assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000000}), @@ -414,7 +417,6 @@ public class ImportHostingAssets extends ImportOfficeData { """); } - // ============================================================================================ @Test @@ -697,7 +699,7 @@ public class ImportHostingAssets extends ImportOfficeData { entry("target", targets) )) .build(); - hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); + hostingAssets.put(EMAILALIAS_ID_OFFSET + unixuser_id, unixUserAsset); }); } -- 2.39.5 From 6c49ba2478a73139a04bf13ff3b3b856b73cfba5 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 31 Jul 2024 05:44:40 +0200 Subject: [PATCH 06/14] unixuser+emailaliases import generally working with test data --- .../hs/booking/item/HsBookingItemEntity.java | 9 ++ .../HostingAssetEntitySaveProcessor.java | 11 ++ ...HsDomainDnsSetupHostingAssetValidator.java | 2 +- .../HsUnixUserHostingAssetValidator.java | 6 +- .../hs/validation/HsEntityValidator.java | 7 +- .../hs/validation/PasswordProperty.java | 23 ++-- .../hs/validation/PropertiesProvider.java | 5 + .../hs/validation/ValidatableProperty.java | 30 ++++- .../hsadminng/hs/migration/CsvDataImport.java | 13 +- .../hs/migration/ImportHostingAssets.java | 122 ++++++++++++------ .../validation/PasswordPropertyUnitTest.java | 5 + .../migration/hosting/emailalias.csv | 4 + .../resources/migration/hosting/unixuser.csv | 6 +- 13 files changed, 174 insertions(+), 69 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java index 5a0eb885..a9a9c879 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java @@ -32,6 +32,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; +import jakarta.persistence.PostLoad; import jakarta.persistence.Table; import jakarta.persistence.Transient; import jakarta.persistence.Version; @@ -124,6 +125,14 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject resourcesWrapper; + @Transient + private boolean isLoaded; + + @PostLoad + public void markAsLoaded() { + this.isLoaded = true; + } + public PatchableMapWrapper getResources() { return PatchableMapWrapper.of(resourcesWrapper, (newWrapper) -> {resourcesWrapper = newWrapper; }, resources ); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java index 495ea665..9495340e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java @@ -40,6 +40,17 @@ public class HostingAssetEntitySaveProcessor { return this; } + /// validates the entity itself including its properties, but ignoring some error messages for import of legacy data + public HostingAssetEntitySaveProcessor validateEntityIgnoring(final String ignoreRegExp) { + step("validateEntity", "prepareForSave"); + MultiValidationException.throwIfNotEmpty( + validator.validateEntity(entity).stream() + .filter(errorMsg -> !errorMsg.matches(ignoreRegExp)) + .toList() + ); + return this; + } + /// hashing passwords etc. @SuppressWarnings("unchecked") public HostingAssetEntitySaveProcessor prepareForSave() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index b24efe3b..97c44ce2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -18,7 +18,7 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator { // according to RFC 1035 (section 5) and RFC 1034 - static final String RR_REGEX_NAME = "([a-z0-9\\._-]+|@)\\s+"; + static final String RR_REGEX_NAME = "([a-z0-9\\.-]+|@)\\s+"; static final String RR_REGEX_TTL = "(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*"; static final String RR_REGEX_IN = "IN\\s+"; // record class IN for Internet static final String RR_RECORD_TYPE = "[A-Z]+\\s+"; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index 0d913879..965c2d1b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -9,7 +9,6 @@ import jakarta.persistence.EntityManager; import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; -import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty; import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty; @@ -24,16 +23,17 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { AlarmContact.isOptional(), booleanProperty("locked").readOnly(), - integerProperty("userid").computedBy(HsUnixUserHostingAssetValidator::computeUserId), + integerProperty("userid").readOnly().initializedBy(HsUnixUserHostingAssetValidator::computeUserId), integerProperty("SSD hard quota").unit("MB").maxFrom("SSD").withFactor(1024).optional(), integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(), integerProperty("HDD hard quota").unit("MB").maxFrom("HDD").withFactor(1024).optional(), integerProperty("HDD soft quota").unit("MB").maxFrom("HDD hard quota").optional(), stringProperty("shell") + // TODO.spec: do we want to change them all to /usr/bin/, also in import? .provided("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd") .withDefault("/bin/false"), - stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir), + stringProperty("homedir").readOnly().renderedBy(HsUnixUserHostingAssetValidator::computeHomedir), stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(), passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly()); // TODO.spec: public SSH keys? (only if hsadmin-ng is only accessible with 2FA) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java index 741b9d59..77cc2514 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -14,6 +14,9 @@ import java.util.stream.Collectors; import static java.util.Arrays.stream; import static java.util.Collections.emptyList; +import static net.hostsharing.hsadminng.hs.validation.ValidatableProperty.ComputeMode.IN_INIT; +import static net.hostsharing.hsadminng.hs.validation.ValidatableProperty.ComputeMode.IN_PREP; +import static net.hostsharing.hsadminng.hs.validation.ValidatableProperty.ComputeMode.IN_REVAMP; // TODO.refa: rename to HsEntityProcessor, also subclasses public abstract class HsEntityValidator { @@ -109,7 +112,7 @@ public abstract class HsEntityValidator { public void prepareProperties(final EntityManager em, final E entity) { stream(propertyValidators).forEach(p -> { - if (!p.isReadOnly() && p.isComputed()) { + if (p.isComputed(IN_PREP) || p.isComputed(IN_INIT) && !entity.isLoaded() ) { entity.directProps().put(p.propertyName, p.compute(em, entity)); } }); @@ -120,7 +123,7 @@ public abstract class HsEntityValidator { stream(propertyValidators).forEach(p -> { if (p.isWriteOnly()) { copy.remove(p.propertyName); - } else if (p.isReadOnly() && p.isComputed()) { + } else if (p.isComputed(IN_REVAMP)) { copy.put(p.propertyName, p.compute(em, entity)); } }); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java index ac88e7a6..083e69ca 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java @@ -1,8 +1,8 @@ package net.hostsharing.hsadminng.hs.validation; +import lombok.Setter; import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm; -import lombok.Setter; import java.util.List; import java.util.stream.Stream; @@ -13,7 +13,10 @@ import static net.hostsharing.hsadminng.mapper.Array.insertNewEntriesAfterExisti @Setter public class PasswordProperty extends StringProperty { - private static final String[] KEY_ORDER = insertNewEntriesAfterExistingEntry(StringProperty.KEY_ORDER, "computed", "hashedUsing"); + private static final String[] KEY_ORDER = insertNewEntriesAfterExistingEntry( + StringProperty.KEY_ORDER, + "computed", + "hashedUsing"); private Algorithm hashedUsing; @@ -34,10 +37,11 @@ public class PasswordProperty extends StringProperty { public PasswordProperty hashedUsing(final Algorithm algorithm) { this.hashedUsing = algorithm; - computedBy((em, entity) - -> ofNullable(entity.getDirectValue(propertyName, String.class)) - .map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password)) - .orElse(null)); + computedBy( + ComputeMode.IN_PREP, + (em, entity) -> ofNullable(entity.getDirectValue(propertyName, String.class)) + .map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password)) + .orElse(null)); return self(); } @@ -69,9 +73,10 @@ public class PasswordProperty extends StringProperty { } } - final long groupsCovered = Stream.of(hasLowerCase, hasUpperCase, hasDigit, hasSpecialChar).filter(v->v).count(); - if ( groupsCovered < 3) { - result.add(propertyName + "' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"); + final long groupsCovered = Stream.of(hasLowerCase, hasUpperCase, hasDigit, hasSpecialChar).filter(v -> v).count(); + if (groupsCovered < 3) { + result.add(propertyName + + "' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"); } if (containsColon) { result.add(propertyName + "' must not contain colon (':')"); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/PropertiesProvider.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/PropertiesProvider.java index c4d60fb8..363e0126 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/PropertiesProvider.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/PropertiesProvider.java @@ -4,6 +4,7 @@ import java.util.Map; public interface PropertiesProvider { + boolean isLoaded(); Map directProps(); Object getContextValue(final String propName); @@ -11,6 +12,10 @@ public interface PropertiesProvider { return cast(propName, directProps().get(propName), clazz, null); } + default T getDirectValue(final String propName, final Class clazz, final T defaultValue) { + return cast(propName, directProps().get(propName), clazz, defaultValue); + } + default T getContextValue(final String propName, final Class clazz) { return cast(propName, getContextValue(propName), clazz, null); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java index 429f97d5..0d8fa604 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java @@ -48,11 +48,17 @@ public abstract class ValidatableProperty

, T private Set requiresAtMaxOneOf; private T defaultValue; + protected enum ComputeMode { + IN_INIT, + IN_PREP, + IN_REVAMP + } + @JsonIgnore private BiFunction computedBy; @Accessors(makeFinal = true, chain = true, fluent = false) - private boolean computed; // used in descriptor, because computedBy cannot be rendered to a text string + private ComputeMode computed; // name 'computed' instead 'computeMode' for better readability in property description @Accessors(makeFinal = true, chain = true, fluent = false) private boolean readOnly; @@ -77,7 +83,7 @@ public abstract class ValidatableProperty

, T return null; } -protected void setDeferredInit(final Function[], T[]> function) { + protected void setDeferredInit(final Function[], T[]> function) { this.deferredInit = function; } @@ -236,8 +242,8 @@ protected void setDeferredInit(final Function[], T[]> protected abstract void validate(final List result, final T propValue, final PropertiesProvider propProvider); public void verifyConsistency(final Map.Entry, ?> typeDef) { - if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && !computed) { - throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .computed(...), .readOnly(), .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" ); + if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && defaultValue == null) { + throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .readOnly(), .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" ); } } @@ -302,12 +308,24 @@ protected void setDeferredInit(final Function[], T[]> .toList(); } - public P computedBy(final BiFunction compute) { + public P initializedBy(final BiFunction compute) { + return computedBy(ComputeMode.IN_INIT, compute); + } + + public P renderedBy(final BiFunction compute) { + return computedBy(ComputeMode.IN_REVAMP, compute); + } + + protected P computedBy(final ComputeMode computeMode, final BiFunction compute) { this.computedBy = compute; - this.computed = true; + this.computed = computeMode; return self(); } + public boolean isComputed(final ComputeMode computeMode) { + return computed == computeMode; + } + public T compute(final EntityManager em, final E entity) { return computedBy.apply(em, entity); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java index d6ee39d0..b124c08a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -26,6 +26,8 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; @@ -230,7 +232,7 @@ public class CsvDataImport extends ContextBasedTest { } void logErrors() { - assumeThat(errors).isEmpty(); + assertThat(errors).isEmpty(); } } @@ -298,12 +300,17 @@ class Record { } } +@Retention(RetentionPolicy.RUNTIME) +@interface ContinueOnFailure { +} + class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { private static boolean previousTestsPassed = true; - public void testFailed(ExtensionContext context, Throwable cause) { - previousTestsPassed = false; + @Override + public void testFailed(final ExtensionContext context, final Throwable cause) { + previousTestsPassed = previousTestsPassed && context.getElement().map(e -> e.isAnnotationPresent(ContinueOnFailure.class)).orElse(false); } @Override diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index beeb3391..ddcf27da 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -287,20 +287,20 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null values assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { - 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), - 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}), - 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102093}), - 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), - 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), - 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), - 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}), - 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}), - 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}), + 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), + 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}), + 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "shell": "/bin/bash", "userid": 102093}), + 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), + 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), + 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), + 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}), + 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}), + 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}), 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 10003}), 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}), 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}), - 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), - 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) + 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), + 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) } """); } @@ -324,12 +324,16 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null values assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace(""" { - 4002403=HsHostingAssetEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), - 4002405=HsHostingAssetEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}), - 4002429=HsHostingAssetEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}), - 4002431=HsHostingAssetEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}), - 4002449=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}), - 4002451=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}) + 5002403=HsHostingAssetEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), + 5002405=HsHostingAssetEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}), + 5002429=HsHostingAssetEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}), + 5002431=HsHostingAssetEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}), + 5002449=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}), + 5002451=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}), + 5002452=HsHostingAssetEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { "target": "[]"}), + 5002453=HsHostingAssetEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]"}), + 5002454=HsHostingAssetEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { "target": "[/dev/null]"}), + 5002455=HsHostingAssetEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"}) } """); } @@ -337,7 +341,7 @@ public class ImportHostingAssets extends ImportOfficeData { // -------------------------------------------------------------------------------------------- @Test - @Order(11400) + @Order(18010) void validateBookingItems() { bookingItems.forEach((id, bi) -> { try { @@ -349,19 +353,27 @@ public class ImportHostingAssets extends ImportOfficeData { } @Test - @Order(11410) + @Order(18020) void validateHostingAssets() { hostingAssets.forEach((id, ha) -> { try { new HostingAssetEntitySaveProcessor(em, ha) .preprocessEntity() - .validateEntity(); + .validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*") + .prepareForSave(); } catch (final Exception exc) { errors.add("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage()); } }); } + @Test + @Order(18999) + @ContinueOnFailure + void logValidationErrors() { + super.logErrors(); + } + // -------------------------------------------------------------------------------------------- @Test @@ -389,30 +401,28 @@ public class ImportHostingAssets extends ImportOfficeData { persistHostingAssetsOfType(EMAIL_ALIAS); } - @Test @Order(19010) void verifyPersistedUnixUsersWithUserId() { assumeThatWeAreImportingControlledTestData(); // no contacts yet => mostly null value - // FIXME: keep original userids assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { - 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000000}), - 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000001}), - 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000002}), - 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000003}), - 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000004}), - 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000005}), - 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000006}), - 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000007}), - 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000008}), - 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 100000009}), - 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000010}), - 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 100000011}), - 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000012}), - 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000013}) + 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102090}), + 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102091}), + 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102093}), + 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102094}), + 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102095}), + 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102106}), + 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102147}), + 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102148}), + 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102160}), + 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 10003}), + 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 10000}), + 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 110568}), + 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110593}), + 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110594}) } """); } @@ -436,12 +446,12 @@ public class ImportHostingAssets extends ImportOfficeData { private void persistHostingAssetsOfType(final HsHostingAssetType hsHostingAssetType) { jpaAttempt.transacted(() -> { - context(rbacSuperuser); hostingAssets.forEach((key, ha) -> { + context(rbacSuperuser); if (ha.getType() == hsHostingAssetType) { new HostingAssetEntitySaveProcessor(em, ha) .preprocessEntity() - .validateEntity() + .validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*") .prepareForSave() .saveUsing(entity -> persist(key, entity)) .validateContext(); @@ -522,8 +532,8 @@ public class ImportHostingAssets extends ImportOfficeData { .isTrue()); final var asset = HsHostingAssetEntity.builder() - .isLoaded(haType - == MANAGED_WEBSPACE) // this turns off identifier validation to accept former default prefixes + // this turns off identifier validation to accept former default prefixes + .isLoaded(haType == MANAGED_WEBSPACE) .type(haType) .identifier(packet_name) .bookingItem(bookingItem) @@ -663,9 +673,10 @@ public class ImportHostingAssets extends ImportOfficeData { final var packet_id = rec.getInteger("packet_id"); final var unixUserAsset = HsHostingAssetEntity.builder() .type(UNIX_USER) - .parentAsset(hostingAssets.get(PACKET_ID_OFFSET+packet_id)) + .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .identifier(rec.getString("name")) .caption(rec.getString("comment")) + .isLoaded(true) // avoid overwriting imported userids with generated ids .config(new HashMap<>(Map.ofEntries( entry("shell", rec.getString("shell")), // entry("homedir", rec.getString("homedir")), do not import, it's calculated @@ -677,6 +688,33 @@ public class ImportHostingAssets extends ImportOfficeData { entry("HDD hard quota", rec.getInteger("storage_hardlimit")) ))) .build(); + + // TODO.spec: crop SSD+HDD limits if > booked + if (unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0) + > 1024*unixUserAsset.getContextValue("SSD", Integer.class, 0)) { + unixUserAsset.getConfig().put("SSD hard quota", unixUserAsset.getContextValue("SSD", Integer.class, 0)*1024); + } + if (unixUserAsset.getDirectValue("HDD hard quota", Integer.class, 0) + > 1024*unixUserAsset.getContextValue("HDD", Integer.class, 0)) { + unixUserAsset.getConfig().put("HDD hard quota", unixUserAsset.getContextValue("HDD", Integer.class, 0)*1024); + } + + // TODO.spec: does `softlimit unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0)) { + unixUserAsset.getConfig().put("SSD soft quota", unixUserAsset.getConfig().get("SSD hard quota")); + } + if (unixUserAsset.getDirectValue("HDD soft quota", Integer.class, 0) + > unixUserAsset.getDirectValue("HDD hard quota", Integer.class, 0)) { + unixUserAsset.getConfig().put("HDD soft quota", unixUserAsset.getConfig().get("HDD hard quota")); + } + + // TODO.spec: remove HDD limits if no HDD storage is booked + if (unixUserAsset.getContextValue("HDD", Integer.class, 0) == 0) { + unixUserAsset.getConfig().remove("HDD hard quota"); + unixUserAsset.getConfig().remove("HDD soft quota"); + } + hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); }); } @@ -692,7 +730,7 @@ public class ImportHostingAssets extends ImportOfficeData { final var targets = parseCsvLine(rec.getString("target")); final var unixUserAsset = HsHostingAssetEntity.builder() .type(EMAIL_ALIAS) - .parentAsset(hostingAssets.get(PACKET_ID_OFFSET+packet_id)) + .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .identifier(rec.getString("name")) .caption(rec.getString("name")) .config(Map.ofEntries( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java index 47e40336..aea913e5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java @@ -103,6 +103,11 @@ class PasswordPropertyUnitTest { // when final var result = passwordProp.compute(em, new PropertiesProvider() { + @Override + public boolean isLoaded() { + return false; + } + @Override public Map directProps() { return Map.ofEntries( diff --git a/src/test/resources/migration/hosting/emailalias.csv b/src/test/resources/migration/hosting/emailalias.csv index 7701c61a..7d5cd887 100644 --- a/src/test/resources/migration/hosting/emailalias.csv +++ b/src/test/resources/migration/hosting/emailalias.csv @@ -5,3 +5,7 @@ emailalias_id;pac_id;name;target 2431;1112;mim00-abruf;michael.mellis@hostsharing.net 2449;1112;mim00-hhfx;"mim00-hhfx,""|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l""" 2451;1112;mim00-hhfx-l;:include:/home/pacs/mim00/etc/hhfx.list +2452;1112;mim00-empty; +2453;1112;mim00-0_entries;"" +2454;1112;mim00-dev.null; /dev/null +2455;1112;mim00-1_with_space;" ""|/home/pacs/mim00/install/corpslistar/listar""" diff --git a/src/test/resources/migration/hosting/unixuser.csv b/src/test/resources/migration/hosting/unixuser.csv index ee08e2f0..68538a04 100644 --- a/src/test/resources/migration/hosting/unixuser.csv +++ b/src/test/resources/migration/hosting/unixuser.csv @@ -2,11 +2,11 @@ unixuser_id;name;comment;shell;homedir;locked;packet_id;userid;quota_softlimit;q 100824;hsh00;Hostsharing Paket;/bin/bash;/home/pacs/hsh00;0;630;10000;0;0;0;0 5803;lug00;LUGs;/bin/bash;/home/pacs/lug00;0;1094;102090;0;0;0;0 -5805;lug00-wla.1;Paul Klemm;/bin/bash;/home/pacs/lug00/users/deaf;0;1094;102091;0;0;0;0 -5809;lug00-wla.2;Walter Müller;/bin/bash;/home/pacs/lug00/users/marl;0;1094;102093;0;0;0;0 +5805;lug00-wla.1;Paul Klemm;/bin/bash;/home/pacs/lug00/users/deaf;0;1094;102091;4;0;0;0 +5809;lug00-wla.2;Walter Müller;/bin/bash;/home/pacs/lug00/users/marl;0;1094;102093;4;8;0;0 5811;lug00-ola.a;LUG OLA - POP a;/usr/bin/passwd;/home/pacs/lug00/users/marl.a;1;1094;102094;0;0;0;0 5813;lug00-ola.b;LUG OLA - POP b;/usr/bin/passwd;/home/pacs/lug00/users/marl.b;1;1094;102095;0;0;0;0 -5835;lug00-test;Test;/usr/bin/passwd;/home/pacs/lug00/users/test;0;1094;102106;0;0;0;0 +5835;lug00-test;Test;/usr/bin/passwd;/home/pacs/lug00/users/test;0;1094;102106;2000000;4000000;20;0 100705;hsh00-mim;Michael Mellis;/bin/false;/home/pacs/hsh00/users/mi;0;630;10003;0;0;0;0 5964;mim00;Michael Mellis;/bin/bash;/home/pacs/mim00;0;1112;102147;0;0;0;0 -- 2.39.5 From 89808cecf70479e59e73ae2f55c4741d2f44867e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 31 Jul 2024 12:13:55 +0200 Subject: [PATCH 07/14] use HsHostingAssetRawEntity for ImportHostingAssets --- .../hs/hosting/asset/HsHostingAsset.java | 69 ++++++++ .../hosting/asset/HsHostingAssetEntity.java | 50 +----- .../asset/HsHostingAssetRepository.java | 2 +- .../hs/hosting/asset/HsHostingAssetType.java | 16 +- .../HostingAssetEntitySaveProcessor.java | 14 +- .../HostingAssetEntityValidator.java | 50 +++--- .../HostingAssetEntityValidatorRegistry.java | 9 +- .../HsCloudServerHostingAssetValidator.java | 4 +- ...HsDomainDnsSetupHostingAssetValidator.java | 12 +- ...sDomainHttpSetupHostingAssetValidator.java | 6 +- ...sDomainMboxSetupHostingAssetValidator.java | 6 +- .../HsDomainSetupHostingAssetValidator.java | 6 +- ...sDomainSmtpSetupHostingAssetValidator.java | 6 +- .../HsEMailAddressHostingAssetValidator.java | 8 +- .../HsEMailAliasHostingAssetValidator.java | 6 +- .../HsIPv4NumberHostingAssetValidator.java | 4 +- .../HsIPv6NumberHostingAssetValidator.java | 6 +- .../HsManagedServerHostingAssetValidator.java | 4 +- ...sManagedWebspaceHostingAssetValidator.java | 4 +- ...sMariaDbDatabaseHostingAssetValidator.java | 4 +- ...sMariaDbInstanceHostingAssetValidator.java | 6 +- .../HsMariaDbUserHostingAssetValidator.java | 4 +- ...stgreSqlDatabaseHostingAssetValidator.java | 4 +- ...greSqlDbInstanceHostingAssetValidator.java | 6 +- ...HsPostgreSqlUserHostingAssetValidator.java | 4 +- .../HsUnixUserHostingAssetValidator.java | 6 +- .../hs/hosting/asset/validators/README.md | 10 +- .../hsadminng/stringify/Stringify.java | 27 ++- ...DnsSetupHostingAssetValidatorUnitTest.java | 6 +- ...ailAliasHostingAssetValidatorUnitTest.java | 6 +- ...iaDbUserHostingAssetValidatorUnitTest.java | 2 +- ...eSqlUserHostingAssetValidatorUnitTest.java | 2 +- ...UnixUserHostingAssetValidatorUnitTest.java | 43 +++-- .../hsadminng/hs/migration/CsvDataImport.java | 23 +++ .../hs/migration/HsHostingAssetRawEntity.java | 114 +++++++++++++ .../hs/migration/ImportHostingAssets.java | 159 ++++++++++-------- 36 files changed, 461 insertions(+), 247 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/migration/HsHostingAssetRawEntity.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java new file mode 100644 index 00000000..afa3b0e9 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java @@ -0,0 +1,69 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; +import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; +import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; +import net.hostsharing.hsadminng.stringify.Stringify; +import net.hostsharing.hsadminng.stringify.Stringifyable; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static java.util.Collections.emptyMap; +import static net.hostsharing.hsadminng.stringify.Stringify.stringify; + +public interface HsHostingAsset extends Stringifyable, RbacObject, PropertiesProvider { + + Stringify stringify = stringify(HsHostingAsset.class) + .withProp(HsHostingAsset::getType) + .withProp(HsHostingAsset::getIdentifier) + .withProp(HsHostingAsset::getCaption) + .withProp(HsHostingAsset::getParentAsset) + .withProp(HsHostingAsset::getAssignedToAsset) + .withProp(HsHostingAsset::getBookingItem) + .withProp(HsHostingAsset::getConfig) + .quotedValues(false); + + HsHostingAssetType getType(); + HsHostingAsset getParentAsset(); + void setIdentifier(String s); + String getIdentifier(); + HsBookingItemEntity getBookingItem(); + HsHostingAsset getAssignedToAsset(); + HsOfficeContactEntity getAlarmContact(); + List getSubHostingAssets(); + String getCaption(); + Map getConfig(); + + default HsBookingProjectEntity getRelatedProject() { + return Optional.ofNullable(getBookingItem()) + .map(HsBookingItemEntity::getRelatedProject) + .orElseGet(() -> Optional.ofNullable(getParentAsset()) + .map(HsHostingAsset::getRelatedProject) + .orElse(null)); + } + + @Override + default Object getContextValue(final String propName) { + final var v = directProps().get(propName); + if (v != null) { + return v; + } + + if (getBookingItem() != null) { + return getBookingItem().getResources().get(propName); + } + if (getParentAsset() != null && getParentAsset().getBookingItem() != null) { + return getParentAsset().getBookingItem().getResources().get(propName); + } + return emptyMap(); + } + + @Override + default String toShortString() { + return getType() + ":" + getIdentifier(); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index 6965d82f..ceb27238 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java @@ -8,15 +8,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; -import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; -import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; -import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; -import net.hostsharing.hsadminng.stringify.Stringify; -import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.Type; import jakarta.persistence.CascadeType; @@ -39,10 +34,8 @@ import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.UUID; -import static java.util.Collections.emptyMap; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase; @@ -70,17 +63,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Setter @NoArgsConstructor @AllArgsConstructor -public class HsHostingAssetEntity implements Stringifyable, RbacObject, PropertiesProvider { - - private static Stringify stringify = stringify(HsHostingAssetEntity.class) - .withProp(HsHostingAssetEntity::getType) - .withProp(HsHostingAssetEntity::getIdentifier) - .withProp(HsHostingAssetEntity::getCaption) - .withProp(HsHostingAssetEntity::getParentAsset) - .withProp(HsHostingAssetEntity::getAssignedToAsset) - .withProp(HsHostingAssetEntity::getBookingItem) - .withProp(HsHostingAssetEntity::getConfig) - .quotedValues(false); +public class HsHostingAssetEntity implements HsHostingAsset { @Id @GeneratedValue @@ -136,14 +119,6 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject Optional.ofNullable(parentAsset) - .map(HsHostingAssetEntity::getRelatedProject) - .orElse(null)); - } - public PatchableMapWrapper getConfig() { return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); } @@ -157,30 +132,9 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject { " *==> "); } - static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { + static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { return new EntityTypeRelation<>( OPTIONAL, PARENT_ASSET, - HsHostingAssetEntity::getParentAsset, + HsHostingAsset::getParentAsset, hostingAssetType, " o..> "); } - static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { + static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { return new EntityTypeRelation<>( REQUIRED, PARENT_ASSET, - HsHostingAssetEntity::getParentAsset, + HsHostingAsset::getParentAsset, hostingAssetType, " *==> "); } - static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { + static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { return new EntityTypeRelation<>( REQUIRED, ASSIGNED_TO_ASSET, - HsHostingAssetEntity::getAssignedToAsset, + HsHostingAsset::getAssignedToAsset, hostingAssetType, " o--> "); } @@ -416,11 +416,11 @@ class EntityTypeRelation { return this; } - static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { + static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { return new EntityTypeRelation<>( OPTIONAL, ASSIGNED_TO_ASSET, - HsHostingAssetEntity::getAssignedToAsset, + HsHostingAsset::getAssignedToAsset, hostingAssetType, " o..> "); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java index 9495340e..5d7b9ddd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.errors.MultiValidationException; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; @@ -10,17 +10,17 @@ import java.util.Map; import java.util.function.Function; /** - * Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsHostingAssetEntity into a readable API. + * Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsHostingAsset into a readable API. */ public class HostingAssetEntitySaveProcessor { - private final HsEntityValidator validator; + private final HsEntityValidator validator; private String expectedStep = "preprocessEntity"; private final EntityManager em; - private HsHostingAssetEntity entity; + private HsHostingAsset entity; private HsHostingAssetResource resource; - public HostingAssetEntitySaveProcessor(final EntityManager em, final HsHostingAssetEntity entity) { + public HostingAssetEntitySaveProcessor(final EntityManager em, final HsHostingAsset entity) { this.em = em; this.entity = entity; this.validator = HostingAssetEntityValidatorRegistry.forType(entity.getType()); @@ -59,7 +59,7 @@ public class HostingAssetEntitySaveProcessor { return this; } - public HostingAssetEntitySaveProcessor saveUsing(final Function saveFunction) { + public HostingAssetEntitySaveProcessor saveUsing(final Function saveFunction) { step("saveUsing", "validateContext"); entity = saveFunction.apply(entity); return this; @@ -74,7 +74,7 @@ public class HostingAssetEntitySaveProcessor { /// maps entity to JSON resource representation public HostingAssetEntitySaveProcessor mapUsing( - final Function mapFunction) { + final Function mapFunction) { step("mapUsing", "revampProperties"); resource = mapFunction.apply(entity); return this; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java index 6433814c..b6747ff8 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; @@ -23,13 +23,13 @@ import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; -public abstract class HostingAssetEntityValidator extends HsEntityValidator { +public abstract class HostingAssetEntityValidator extends HsEntityValidator { static final ValidatableProperty[] NO_EXTRA_PROPERTIES = new ValidatableProperty[0]; private final ReferenceValidator bookingItemReferenceValidation; - private final ReferenceValidator parentAssetReferenceValidation; - private final ReferenceValidator assignedToAssetReferenceValidation; + private final ReferenceValidator parentAssetReferenceValidation; + private final ReferenceValidator assignedToAssetReferenceValidation; private final HostingAssetEntityValidator.AlarmContact alarmContactValidation; HostingAssetEntityValidator( @@ -40,23 +40,23 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator( assetType.bookingItemPolicy(), assetType.bookingItemTypes(), - HsHostingAssetEntity::getBookingItem, + HsHostingAsset::getBookingItem, HsBookingItemEntity::getType); this.parentAssetReferenceValidation = new ReferenceValidator<>( assetType.parentAssetPolicy(), assetType.parentAssetTypes(), - HsHostingAssetEntity::getParentAsset, - HsHostingAssetEntity::getType); + HsHostingAsset::getParentAsset, + HsHostingAsset::getType); this.assignedToAssetReferenceValidation = new ReferenceValidator<>( assetType.assignedToAssetPolicy(), assetType.assignedToAssetTypes(), - HsHostingAssetEntity::getAssignedToAsset, - HsHostingAssetEntity::getType); + HsHostingAsset::getAssignedToAsset, + HsHostingAsset::getType); this.alarmContactValidation = alarmContactValidation; } @Override - public List validateEntity(final HsHostingAssetEntity assetEntity) { + public List validateEntity(final HsHostingAsset assetEntity) { return sequentiallyValidate( () -> validateEntityReferencesAndProperties(assetEntity), () -> validateIdentifierPattern(assetEntity) @@ -64,7 +64,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator validateContext(final HsHostingAssetEntity assetEntity) { + public List validateContext(final HsHostingAsset assetEntity) { return sequentiallyValidate( () -> optionallyValidate(assetEntity.getBookingItem()), () -> optionallyValidate(assetEntity.getParentAsset()), @@ -72,7 +72,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator validateEntityReferencesAndProperties(final HsHostingAssetEntity assetEntity) { + private List validateEntityReferencesAndProperties(final HsHostingAsset assetEntity) { return Stream.of( validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate), validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate), @@ -86,17 +86,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator validateReferencedEntity( - final HsHostingAssetEntity assetEntity, + final HsHostingAsset assetEntity, final String referenceFieldName, - final BiFunction> validator) { + final BiFunction> validator) { return enrich(prefix(assetEntity.toShortString()), validator.apply(assetEntity, referenceFieldName)); } - private List validateProperties(final HsHostingAssetEntity assetEntity) { + private List validateProperties(final HsHostingAsset assetEntity) { return enrich(prefix(assetEntity.toShortString(), "config"), super.validateProperties(assetEntity)); } - private static List optionallyValidate(final HsHostingAssetEntity assetEntity) { + private static List optionallyValidate(final HsHostingAsset assetEntity) { return assetEntity != null ? enrich( prefix(assetEntity.toShortString(), "parentAsset"), @@ -112,7 +112,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator validateAgainstSubEntities(final HsHostingAssetEntity assetEntity) { + protected List validateAgainstSubEntities(final HsHostingAsset assetEntity) { return enrich( prefix(assetEntity.toShortString(), "config"), stream(propertyValidators) @@ -124,7 +124,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator propDef) { final var propName = propDef.propertyName(); final var propUnit = ofNullable(propDef.unit()).map(u -> " " + u).orElse(""); @@ -140,7 +140,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator validateIdentifierPattern(final HsHostingAssetEntity assetEntity) { + private List validateIdentifierPattern(final HsHostingAsset assetEntity) { final var expectedIdentifierPattern = identifierPattern(assetEntity); if (assetEntity.getIdentifier() == null || !expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) { @@ -151,19 +151,19 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator { private final HsHostingAssetType.RelationPolicy policy; private final Set referencedEntityTypes; - private final Function referencedEntityGetter; + private final Function referencedEntityGetter; private final Function referencedEntityTypeGetter; public ReferenceValidator( final HsHostingAssetType.RelationPolicy policy, final Set referencedEntityTypes, - final Function referencedEntityGetter, + final Function referencedEntityGetter, final Function referencedEntityTypeGetter) { this.policy = policy; this.referencedEntityTypes = referencedEntityTypes; @@ -173,14 +173,14 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator referencedEntityGetter) { + final Function referencedEntityGetter) { this.policy = policy; this.referencedEntityTypes = Set.of(); this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityTypeGetter = e -> null; } - List validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) { + List validate(final HsHostingAsset assetEntity, final String referenceFieldName) { final var actualEntity = referencedEntityGetter.apply(assetEntity); final var actualEntityType = actualEntity != null ? referencedEntityTypeGetter.apply(actualEntity) : null; @@ -216,7 +216,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator> { AlarmContact(final HsHostingAssetType.RelationPolicy policy) { - super(policy, HsHostingAssetEntity::getAlarmContact); + super(policy, HsHostingAsset::getAlarmContact); } // hostmaster alert address is implicitly added where neccessary diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java index 696beafe..20fef401 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; @@ -12,7 +13,7 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.*; public class HostingAssetEntityValidatorRegistry { - private static final Map, HsEntityValidator> validators = new HashMap<>(); + private static final Map, HsEntityValidator> validators = new HashMap<>(); static { // HOWTO: add (register) new HsHostingAssetType-specific validators register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator()); @@ -36,14 +37,14 @@ public class HostingAssetEntityValidatorRegistry { register(IPV6_NUMBER, new HsIPv6NumberHostingAssetValidator()); } - private static void register(final Enum type, final HsEntityValidator validator) { + private static void register(final Enum type, final HsEntityValidator validator) { stream(validator.propertyValidators).forEach( entry -> { entry.verifyConsistency(Map.entry(type, validator)); }); validators.put(type, validator); } - public static HsEntityValidator forType(final Enum type) { + public static HsEntityValidator forType(final Enum type) { if ( validators.containsKey(type)) { return validators.get(type); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java index 840e5841..b9719a54 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -16,7 +16,7 @@ class HsCloudServerHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index 97c44ce2..052db872 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import lombok.SneakyThrows; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.system.SystemProcess; import java.util.List; @@ -59,12 +59,12 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); if (entity.getIdentifier() == null) { ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); @@ -73,7 +73,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator @Override @SneakyThrows - public List validateContext(final HsHostingAssetEntity assetEntity) { + public List validateContext(final HsHostingAsset assetEntity) { final var result = super.validateContext(assetEntity); // TODO.spec: define which checks should get raised to error level @@ -87,7 +87,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator return result; } - String toZonefileString(final HsHostingAssetEntity assetEntity) { + String toZonefileString(final HsHostingAsset assetEntity) { // TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack return """ $ORIGIN {domain}. @@ -104,7 +104,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator .replace("{userRRs}", getPropertyValues(assetEntity, "user-RR") ); } - private String fqdn(final HsHostingAssetEntity assetEntity) { + private String fqdn(final HsHostingAsset assetEntity) { return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length()-IDENTIFIER_SUFFIX.length()); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainHttpSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainHttpSetupHostingAssetValidator.java index 32a2cb30..37bed650 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainHttpSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainHttpSetupHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -42,12 +42,12 @@ class HsDomainHttpSetupHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); if (entity.getIdentifier() == null) { ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainMboxSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainMboxSetupHostingAssetValidator.java index 0172fda4..41c1aa52 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainMboxSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainMboxSetupHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -20,12 +20,12 @@ class HsDomainMboxSetupHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); if (entity.getIdentifier() == null) { ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java index 17031c5e..cec021a2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.List; import java.util.regex.Pattern; @@ -22,7 +22,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator { } @Override - public List validateEntity(final HsHostingAssetEntity assetEntity) { + public List validateEntity(final HsHostingAsset assetEntity) { // TODO.impl: for newly created entities, check the permission of setting up a domain // // reject, if the domain is any of these: @@ -51,7 +51,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return identifierPattern; } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSmtpSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSmtpSetupHostingAssetValidator.java index e92eba10..bc422029 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSmtpSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSmtpSetupHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -20,12 +20,12 @@ class HsDomainSmtpSetupHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); if (entity.getIdentifier() == null) { ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java index cfaa5eda..3ee8f3d3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAddressHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import java.util.regex.Pattern; @@ -29,7 +29,7 @@ class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator { } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); super.preprocessEntity(entity); if (entity.getIdentifier() == null) { @@ -38,11 +38,11 @@ class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^"+ Pattern.quote(combineIdentifier(assetEntity)) + "$"); } - private static String combineIdentifier(final HsHostingAssetEntity emailAddressAssetEntity) { + private static String combineIdentifier(final HsHostingAsset emailAddressAssetEntity) { return emailAddressAssetEntity.getDirectValue("local-part", String.class) + ofNullable(emailAddressAssetEntity.getDirectValue("sub-domain", String.class)).map(s -> "." + s).orElse("") + "@" + diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java index 5fdb2fb3..ab0cb77c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import java.util.regex.Pattern; @@ -13,7 +13,7 @@ class HsEMailAliasHostingAssetValidator 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 EMAIL_ADDRESS_REGEX = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"; // RFC 5322 private static final String INCLUDE_REGEX = "^:include:/.*$"; - private static final String PIPE_REGEX = "^|.*$"; + private static final String PIPE_REGEX = "^\\|.*$"; public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322 HsEMailAliasHostingAssetValidator() { @@ -26,7 +26,7 @@ class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9][a-z0-9\\._-]*$"); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java index 235a32c2..b237729e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -20,7 +20,7 @@ class HsIPv4NumberHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return IPV4_REGEX; } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java index b910ea82..873a73eb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.net.InetAddress; import java.net.UnknownHostException; @@ -24,7 +24,7 @@ class HsIPv6NumberHostingAssetValidator extends HostingAssetEntityValidator { } @Override - public List validateEntity(final HsHostingAssetEntity assetEntity) { + public List validateEntity(final HsHostingAsset assetEntity) { final var violations = super.validateEntity(assetEntity); if (!isValidIPv6Address(assetEntity.getIdentifier())) { @@ -35,7 +35,7 @@ class HsIPv6NumberHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return IPV6_REGEX; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java index 732c0285..99138e0e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -54,7 +54,7 @@ class HsManagedServerHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index b962e655..dc0ece36 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -15,7 +15,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var prefixPattern = !assetEntity.isLoaded() ? assetEntity.getRelatedProject().getDebitor().getDefaultPrefix() diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidator.java index 197dc9b6..48618be3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -18,7 +18,7 @@ class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java index 74acd9e6..d9509906 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -19,7 +19,7 @@ class HsMariaDbInstanceHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile( "^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX) @@ -27,7 +27,7 @@ class HsMariaDbInstanceHostingAssetValidator extends HostingAssetEntityValidator } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); if (entity.getIdentifier() == null) { ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier( diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java index 8e749e44..15ae0b45 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hash.HashGenerator; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -26,7 +26,7 @@ class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidator.java index 86e9900e..57d302d0 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -21,7 +21,7 @@ class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValida } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDbInstanceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDbInstanceHostingAssetValidator.java index ecdd5441..36365597 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDbInstanceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDbInstanceHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -21,7 +21,7 @@ class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityVali } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { return Pattern.compile( "^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX) @@ -29,7 +29,7 @@ class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityVali } @Override - public void preprocessEntity(final HsHostingAssetEntity entity) { + public void preprocessEntity(final HsHostingAsset entity) { super.preprocessEntity(entity); if (entity.getIdentifier() == null) { ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier( diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidator.java index 8c91427d..7d527892 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidator.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hash.HashGenerator; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; @@ -26,7 +26,7 @@ class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index 965c2d1b..a53b536f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hash.HashGenerator; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; @@ -40,13 +40,13 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { } @Override - protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\._-]+$"); } private static String computeHomedir(final EntityManager em, final PropertiesProvider propertiesProvider) { - final var entity = (HsHostingAssetEntity) propertiesProvider; + final var entity = (HsHostingAsset) propertiesProvider; final var webspaceName = entity.getParentAsset().getIdentifier(); return "/home/pacs/" + webspaceName + "/users/" + entity.getIdentifier().substring(webspaceName.length()+DASH_LENGTH); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/README.md b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/README.md index 52e03058..72470290 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/README.md +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/README.md @@ -1,9 +1,9 @@ -### HsHostingAssetEntity-Validation +### HsHostingAsset-Validation -There is just a single `HsHostingAssetEntity` class for all types of hosting assets like Managed-Server, Managed-Webspace, Unix-Users, Databases etc. These are distinguished by `HsHostingAssetType HsHostingAssetEntity.type`. +There is just a single `HsHostingAsset` interface and `HsHostingAssetEntity` entity for all types of hosting assets like Managed-Server, Managed-Webspace, Unix-Users, Databases etc. These are distinguished by `HsHostingAssetType HsHostingAsset.type`. For each of these types, a distinct validator has to be -implemented as a subclass of `HsHostingAssetEntityValidator` which needs to be registered (see `HsHostingAssetEntityValidatorRegistry`) for the relevant type(s). +implemented as a subclass of `HsHostingAssetValidator` which needs to be registered (see `HsHostingAssetValidatorRegistry`) for the relevant type(s). ### Kinds of Validations @@ -21,7 +21,7 @@ References in this context are: - the Assigned-To-Hosting-Asset and - the Contact. -The first parameters of the `HsHostingAssetEntityValidator` superclass take rule descriptors for these references. These are all Subclasses fo +The first parameters of the `HsHostingAssetValidator` superclass take rule descriptors for these references. These are all Subclasses fo ### Validation Order @@ -37,4 +37,4 @@ In general, the validation es executed in this order: 2. the limits of the parent entity (parent asset + booking item) 3. limits against the own own-sub-entities -This implementation can be found in `HsHostingAssetEntityValidator.validate`. +This implementation can be found in `HsHostingAssetValidator.validate`. diff --git a/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java b/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java index ffdb7a5a..b410465f 100644 --- a/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java +++ b/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java @@ -16,9 +16,8 @@ import static java.lang.Boolean.TRUE; public final class Stringify { - private final Class clazz; private final String name; - private Function idProp; + private Function idProp; private final List> props = new ArrayList<>(); private String separator = ", "; private Boolean quotedValues = null; @@ -31,8 +30,16 @@ public final class Stringify { return new Stringify<>(clazz, null); } + public Stringify using(final Class subClass) { + //noinspection unchecked + return (Stringify) new Stringify(subClass, null) + .withIdProp(cast(idProp)) + .withProps(cast(props)) + .withSeparator(separator) + .quotedValues(quotedValues); + } + private Stringify(final Class clazz, final String name) { - this.clazz = clazz; if (name != null) { this.name = name; } else { @@ -45,7 +52,7 @@ public final class Stringify { } } - public Stringify withIdProp(final Function getter) { + public Stringify withIdProp(final Function getter) { idProp = getter; return this; } @@ -60,6 +67,11 @@ public final class Stringify { return this; } + private Stringify withProps(final List> props) { + this.props.addAll(props); + return this; + } + public String apply(@NotNull B object) { final var propValues = props.stream() .map(prop -> PropertyValue.of(prop, prop.getter.apply(object))) @@ -74,7 +86,7 @@ public final class Stringify { .map(propVal -> propName(propVal, "=") + optionallyQuoted(propVal)) .collect(Collectors.joining(separator)); return idProp != null - ? name + "(" + idProp.apply(object) + ": " + propValues + ")" + ? name + "(" + idProp.apply(cast(object)) + ": " + propValues + ")" : name + "(" + propValues + ")"; } @@ -106,6 +118,11 @@ public final class Stringify { return this; } + private T cast(final Object object) { + //noinspection unchecked + return (T)object; + } + private record Property(String name, Function getter) {} private record PropertyValue(Property prop, Object rawValue, String value) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index cfe99ae7..7f66379c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -68,7 +68,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { "{type=boolean, propertyName=auto-WILDCARD-AAAA-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-DKIM-RR, defaultValue=true}", "{type=boolean, propertyName=auto-WILDCARD-SPF-RR, defaultValue=true}", - "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[([a-z0-9\\._-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\._-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*], required=true}}" + "{type=string[], propertyName=user-RR, elementsOf={type=string, propertyName=user-RR, matchesRegEx=[([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*], required=true}}" ); } @@ -166,8 +166,8 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( "'DOMAIN_DNS_SETUP:example.org|DNS.config.TTL' is expected to be of type Integer, but is of type String", - "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\._-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\._-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", - "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\._-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\._-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); + "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", + "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java index 19e50d24..9d9422ab 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java @@ -22,7 +22,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( - "{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.-]+$, ^:include:/.*$, ^|.*$], 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.-]+$, ^:include:/.*$, ^\\|.*$], maxLength=320}, required=true, minLength=1}"); } @Test @@ -63,7 +63,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'EMAIL_ALIAS:xyz00-office.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"); + "'EMAIL_ALIAS:xyz00-office.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.-]+$, ^:include:/.*$, ^\\|.*$] but 'garbage' does not match any"); } @Test @@ -84,7 +84,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'identifier' expected to match '^xyz00$|^xyz00-[a-z0-9]+$', but is 'abc00-office'"); + "'identifier' expected to match '^xyz00$|^xyz00-[a-z0-9][a-z0-9\\._-]*$', but is 'abc00-office'"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java index 259d980e..97c8429b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java @@ -49,7 +49,7 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { // then assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( - "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=MYSQL_NATIVE, undisclosed=true}" + "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=IN_PREP, hashedUsing=MYSQL_NATIVE, undisclosed=true}" ); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java index 4b48dc5e..588631c2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlUserHostingAssetValidatorUnitTest.java @@ -52,7 +52,7 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest { // then assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( - "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SCRAM_SHA256, undisclosed=true}" + "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=IN_PREP, hashedUsing=SCRAM_SHA256, undisclosed=true}" ); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java index 7a27db88..7ae081c2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java @@ -3,9 +3,14 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import java.util.HashMap; import java.util.stream.Stream; @@ -16,7 +21,10 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANA import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; import static net.hostsharing.hsadminng.mapper.PatchMap.entry; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +@ExtendWith(MockitoExtension.class) class HsUnixUserHostingAssetValidatorUnitTest { private final HsHostingAssetEntity TEST_MANAGED_SERVER_HOSTING_ASSET = HsHostingAssetEntity.builder() @@ -44,7 +52,17 @@ class HsUnixUserHostingAssetValidatorUnitTest { ))) .build(); - private EntityManager em = null; // not actually needed in these test cases + @Mock + EntityManager em; + + @BeforeEach + void initMocks() { + final var nativeQueryMock = mock(Query.class); + lenient().when(nativeQueryMock.getSingleResult()).thenReturn(12345678); + lenient().when(em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')", Integer.class)) + .thenReturn(nativeQueryMock); + + } @Test void preparesUnixUser() { @@ -61,7 +79,8 @@ class HsUnixUserHostingAssetValidatorUnitTest { entry("SSD hard quota", 50), entry("SSD soft quota", 40), entry("totpKey", "0x123456789abcdef01234"), - entry("password", "$6$Ly3LbsArtL5u4EVt$i/ayIEvm0y4bjkFB6wbg8imbRIaw4mAA4gqYRVyoSkj.iIxJKS3KiRkSjP8gweNcpKL0Q0N31EadT8fCnWErL.") + entry("password", "$6$Ly3LbsArtL5u4EVt$i/ayIEvm0y4bjkFB6wbg8imbRIaw4mAA4gqYRVyoSkj.iIxJKS3KiRkSjP8gweNcpKL0Q0N31EadT8fCnWErL."), + entry("userid", 12345678) )); } @@ -107,11 +126,9 @@ class HsUnixUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'UNIX_USER:abc00-temp.config.SSD hard quota' is expected to be at most 50 but is 100", "'UNIX_USER:abc00-temp.config.SSD soft quota' is expected to be at most 100 but is 200", "'UNIX_USER:abc00-temp.config.HDD hard quota' is expected to be at most 0 but is 100", "'UNIX_USER:abc00-temp.config.HDD soft quota' is expected to be at most 100 but is 200", - "'UNIX_USER:abc00-temp.config.shell' is expected to be one of [/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd] but is '/is/invalid'", "'UNIX_USER:abc00-temp.config.homedir' is readonly but given as '/is/read-only'", "'UNIX_USER:abc00-temp.config.totpKey' is expected to match any of [^0x([0-9A-Fa-f]{2})+$] but provided value does not match", "'UNIX_USER:abc00-temp.config.password' length is expected to be at min 8 but length of provided value is 5", @@ -134,7 +151,7 @@ class HsUnixUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^abc00$|^abc00-[a-z0-9]+$', but is 'xyz99-temp'"); + "'identifier' expected to match '^abc00$|^abc00-[a-z0-9\\._-]+$', but is 'xyz99-temp'"); } @Test @@ -165,14 +182,16 @@ class HsUnixUserHostingAssetValidatorUnitTest { // then assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( - "{type=integer, propertyName=SSD hard quota, unit=GB, maxFrom=SSD}", - "{type=integer, propertyName=SSD soft quota, unit=GB, maxFrom=SSD hard quota}", - "{type=integer, propertyName=HDD hard quota, unit=GB, maxFrom=HDD}", - "{type=integer, propertyName=HDD soft quota, unit=GB, maxFrom=HDD hard quota}", - "{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}", - "{type=string, propertyName=homedir, readOnly=true, computed=true}", + "{type=boolean, propertyName=locked, readOnly=true}", + "{type=integer, propertyName=userid, readOnly=true, computed=IN_INIT}", + "{type=integer, propertyName=SSD hard quota, unit=MB, maxFrom=SSD}", + "{type=integer, propertyName=SSD soft quota, unit=MB, maxFrom=SSD hard quota}", + "{type=integer, propertyName=HDD hard quota, unit=MB, maxFrom=HDD}", + "{type=integer, propertyName=HDD soft quota, unit=MB, maxFrom=HDD hard quota}", + "{type=string, propertyName=shell, provided=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}", + "{type=string, propertyName=homedir, readOnly=true, computed=IN_REVAMP}", "{type=string, propertyName=totpKey, matchesRegEx=[^0x([0-9A-Fa-f]{2})+$], minLength=20, maxLength=256, writeOnly=true, undisclosed=true}", - "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=LINUX_SHA512, undisclosed=true}" + "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=IN_PREP, hashedUsing=LINUX_SHA512, undisclosed=true}" ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java index b124c08a..b0f77302 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -31,12 +31,14 @@ import java.lang.annotation.RetentionPolicy; import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; import static java.lang.Boolean.parseBoolean; +import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; @@ -234,6 +236,27 @@ public class CsvDataImport extends ContextBasedTest { void logErrors() { assertThat(errors).isEmpty(); } + + void expectErrors(final String... expectedErrors) { + assertContainsExactlyInAnyOrderIgnoringWhitespace(errors, expectedErrors); + } + + private static class IgnoringWhitespaceComparator implements Comparator { + @Override + public int compare(String s1, String s2) { + return s1.replaceAll("\\s", "").compareTo(s2.replaceAll("\\s", "")); + } + } + + public static void assertContainsExactlyInAnyOrderIgnoringWhitespace(final List expected, final List actual) { + final var sortedExpected = expected.stream().map(m -> m.replaceAll("\\s", "")).toList(); + final var sortedActual = actual.stream().map(m -> m.replaceAll("\\s", "")).toArray(String[]::new); + assertThat(sortedExpected).containsExactlyInAnyOrder(sortedActual); + } + + public static void assertContainsExactlyInAnyOrderIgnoringWhitespace(final List expected, final String... actual) { + assertContainsExactlyInAnyOrderIgnoringWhitespace(expected, asList(actual)); + } } class Columns { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/HsHostingAssetRawEntity.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/HsHostingAssetRawEntity.java new file mode 100644 index 00000000..33e632d5 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/HsHostingAssetRawEntity.java @@ -0,0 +1,114 @@ +package net.hostsharing.hsadminng.hs.migration; + +import io.hypersistence.utils.hibernate.type.json.JsonType; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; +import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; +import org.hibernate.annotations.Type; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.PostLoad; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.persistence.Version; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Builder +@Entity +@Table(name = "hs_hosting_asset") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class HsHostingAssetRawEntity implements HsHostingAsset { + + @Id + @GeneratedValue + private UUID uuid; + + @Version + private int version; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "bookingitemuuid") + private HsBookingItemEntity bookingItem; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parentassetuuid") + private HsHostingAssetRawEntity parentAsset; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "assignedtoassetuuid") + private HsHostingAssetRawEntity assignedToAsset; + + @Column(name = "type") + @Enumerated(EnumType.STRING) + private HsHostingAssetType type; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "alarmcontactuuid") + private HsOfficeContactEntity alarmContact; + + @OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid") + private List subHostingAssets; + + @Column(name = "identifier") + private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc + + @Column(name = "caption") + private String caption; + + @Builder.Default + @Setter(AccessLevel.NONE) + @Type(JsonType.class) + @Column(columnDefinition = "config") + private Map config = new HashMap<>(); + + @Transient + private PatchableMapWrapper configWrapper; + + @Transient + private boolean isLoaded; + + @PostLoad + public void markAsLoaded() { + this.isLoaded = true; + } + + public PatchableMapWrapper getConfig() { + return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); + } + + @Override + public Map directProps() { + return config; + } + + @Override + public String toString() { + return stringify.using(HsHostingAssetRawEntity.class).apply(this); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index ddcf27da..4c152eb5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -6,7 +6,6 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; @@ -99,12 +98,12 @@ public class ImportHostingAssets extends ImportOfficeData { static final Integer UNIXUSER_ID_OFFSET = 4000000; static final Integer EMAILALIAS_ID_OFFSET = 5000000; - record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference serverRef) {} + record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference serverRef) {} static Map bookingProjects = new WriteOnceMap<>(); static Map bookingItems = new WriteOnceMap<>(); static Map hives = new WriteOnceMap<>(); - static Map hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type? + static Map hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type? @Test @Order(11010) @@ -136,11 +135,11 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null values assertThat(firstOfEachType(5, IPV4_NUMBER)).isEqualToIgnoringWhitespace(""" { - 1000363=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.34), - 1000381=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.52), - 1000402=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.73), - 1000433=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.104), - 1000457=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.128) + 1000363=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.34), + 1000381=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.52), + 1000402=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.73), + 1000433=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.104), + 1000457=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.128) } """); } @@ -191,13 +190,13 @@ public class ImportHostingAssets extends ImportOfficeData { assertThat(firstOfEachType(3, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)).isEqualToIgnoringWhitespace(""" { - 3000630=HsHostingAssetEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), - 3000968=HsHostingAssetEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), - 3000978=HsHostingAssetEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), - 3001061=HsHostingAssetEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), - 3001094=HsHostingAssetEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), - 3001112=HsHostingAssetEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), - 3023611=HsHostingAssetEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) + 3000630=HsHostingAssetRawEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), + 3000968=HsHostingAssetRawEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), + 3000978=HsHostingAssetRawEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), + 3001061=HsHostingAssetRawEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), + 3001094=HsHostingAssetRawEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), + 3001112=HsHostingAssetRawEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), + 3023611=HsHostingAssetRawEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) } """); assertThat(firstOfEachType( @@ -237,15 +236,15 @@ public class ImportHostingAssets extends ImportOfficeData { assertThat(firstOfEachType(5, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)) .isEqualToIgnoringWhitespace(""" { - 3000630=HsHostingAssetEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), - 3000968=HsHostingAssetEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), - 3000978=HsHostingAssetEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), - 3001061=HsHostingAssetEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), - 3001094=HsHostingAssetEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), - 3001112=HsHostingAssetEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), - 3001447=HsHostingAssetEntity(MANAGED_SERVER, vm1093, HA vm1093, D-1000000:hsh default project:BI vm1093), - 3019959=HsHostingAssetEntity(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00), - 3023611=HsHostingAssetEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) + 3000630=HsHostingAssetRawEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), + 3000968=HsHostingAssetRawEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), + 3000978=HsHostingAssetRawEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), + 3001061=HsHostingAssetRawEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), + 3001094=HsHostingAssetRawEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), + 3001112=HsHostingAssetRawEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), + 3001447=HsHostingAssetRawEntity(MANAGED_SERVER, vm1093, HA vm1093, D-1000000:hsh default project:BI vm1093), + 3019959=HsHostingAssetRawEntity(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00), + 3023611=HsHostingAssetRawEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) } """); assertThat(firstOfEachType( @@ -287,20 +286,20 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null values assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { - 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), - 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}), - 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "shell": "/bin/bash", "userid": 102093}), - 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), - 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), - 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), - 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}), - 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}), - 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}), - 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 10003}), - 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}), - 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}), - 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), - 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) + 4005803=HsHostingAssetRawEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), + 4005805=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}), + 4005809=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "shell": "/bin/bash", "userid": 102093}), + 4005811=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), + 4005813=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), + 4005835=HsHostingAssetRawEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), + 4005964=HsHostingAssetRawEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}), + 4005966=HsHostingAssetRawEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}), + 4005990=HsHostingAssetRawEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}), + 4100705=HsHostingAssetRawEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 10003}), + 4100824=HsHostingAssetRawEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}), + 4167846=HsHostingAssetRawEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}), + 4169546=HsHostingAssetRawEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), + 4169596=HsHostingAssetRawEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) } """); } @@ -324,16 +323,16 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null values assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace(""" { - 5002403=HsHostingAssetEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), - 5002405=HsHostingAssetEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}), - 5002429=HsHostingAssetEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}), - 5002431=HsHostingAssetEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}), - 5002449=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}), - 5002451=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}), - 5002452=HsHostingAssetEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { "target": "[]"}), - 5002453=HsHostingAssetEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]"}), - 5002454=HsHostingAssetEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { "target": "[/dev/null]"}), - 5002455=HsHostingAssetEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"}) + 5002403=HsHostingAssetRawEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), + 5002405=HsHostingAssetRawEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}), + 5002429=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}), + 5002431=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}), + 5002449=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}), + 5002451=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}), + 5002452=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { "target": "[]"}), + 5002453=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]"}), + 5002454=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { "target": "[/dev/null]"}), + 5002455=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"}) } """); } @@ -359,7 +358,7 @@ public class ImportHostingAssets extends ImportOfficeData { try { new HostingAssetEntitySaveProcessor(em, ha) .preprocessEntity() - .validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*") + .validateEntity() .prepareForSave(); } catch (final Exception exc) { errors.add("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage()); @@ -371,7 +370,7 @@ public class ImportHostingAssets extends ImportOfficeData { @Order(18999) @ContinueOnFailure void logValidationErrors() { - super.logErrors(); + this.logErrors(); } // -------------------------------------------------------------------------------------------- @@ -409,20 +408,20 @@ public class ImportHostingAssets extends ImportOfficeData { // no contacts yet => mostly null value assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { - 4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102090}), - 4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102091}), - 4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102093}), - 4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102094}), - 4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102095}), - 4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102106}), - 4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102147}), - 4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102148}), - 4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102160}), - 4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 10003}), - 4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 10000}), - 4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 110568}), - 4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110593}), - 4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110594}) + 4005803=HsHostingAssetRawEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102090}), + 4005805=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102091}), + 4005809=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102093}), + 4005811=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102094}), + 4005813=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102095}), + 4005835=HsHostingAssetRawEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102106}), + 4005964=HsHostingAssetRawEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102147}), + 4005966=HsHostingAssetRawEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102148}), + 4005990=HsHostingAssetRawEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102160}), + 4100705=HsHostingAssetRawEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 10003}), + 4100824=HsHostingAssetRawEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 10000}), + 4167846=HsHostingAssetRawEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 110568}), + 4169546=HsHostingAssetRawEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110593}), + 4169596=HsHostingAssetRawEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110594}) } """); } @@ -432,7 +431,25 @@ public class ImportHostingAssets extends ImportOfficeData { @Test @Order(99999) void logErrors() { - super.logErrors(); + if (isImportingControlledTestData()) { + super.expectErrors(""" + validation failed for id:5002452( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { + "target": "[]" + } + )): ['EMAIL_ALIAS:mim00-empty.config.target' length is expected to be at min 1 but length of [[]] is 0]""", + """ + validation failed for id:5002453( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { + "target": "[]" + } + )): ['EMAIL_ALIAS:mim00-0_entries.config.target' length is expected to be at min 1 but length of [[]] is 0]""", + """ + validation failed for id:5002454( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { + "target": "[/dev/null]" + })): ['EMAIL_ALIAS:mim00-dev.null.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.-]+$,^:include:/.*$,^\\|.*$] but '/dev/null' does not match any]""" + ); + } else { + super.logErrors(); + } } private void persistRecursively(final Integer key, final HsBookingItemEntity bi) { @@ -467,7 +484,7 @@ public class ImportHostingAssets extends ImportOfficeData { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - final var ipNumber = HsHostingAssetEntity.builder() + final var ipNumber = HsHostingAssetRawEntity.builder() .type(IPV4_NUMBER) .identifier(rec.getString("inet_addr")) .caption(rec.getString("description")) @@ -531,7 +548,7 @@ public class ImportHostingAssets extends ImportOfficeData { + packet_name) .isTrue()); - final var asset = HsHostingAssetEntity.builder() + final var asset = HsHostingAssetRawEntity.builder() // this turns off identifier validation to accept former default prefixes .isLoaded(haType == MANAGED_WEBSPACE) .type(haType) @@ -671,7 +688,7 @@ public class ImportHostingAssets extends ImportOfficeData { .forEach(rec -> { final var unixuser_id = rec.getInteger("unixuser_id"); final var packet_id = rec.getInteger("packet_id"); - final var unixUserAsset = HsHostingAssetEntity.builder() + final var unixUserAsset = HsHostingAssetRawEntity.builder() .type(UNIX_USER) .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .identifier(rec.getString("name")) @@ -728,7 +745,7 @@ public class ImportHostingAssets extends ImportOfficeData { final var unixuser_id = rec.getInteger("emailalias_id"); final var packet_id = rec.getInteger("pac_id"); final var targets = parseCsvLine(rec.getString("target")); - final var unixUserAsset = HsHostingAssetEntity.builder() + final var unixUserAsset = HsHostingAssetRawEntity.builder() .type(EMAIL_ALIAS) .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .identifier(rec.getString("name")) @@ -770,7 +787,7 @@ public class ImportHostingAssets extends ImportOfficeData { }; } - private static HsHostingAssetEntity ipNumber(final Integer inet_addr_id) { + private static HsHostingAssetRawEntity ipNumber(final Integer inet_addr_id) { return inet_addr_id != null ? hostingAssets.get(IP_NUMBER_ID_OFFSET + inet_addr_id) : null; } @@ -778,7 +795,7 @@ public class ImportHostingAssets extends ImportOfficeData { return hive_id != null ? hives.get(HIVE_ID_OFFSET + hive_id) : null; } - private static HsHostingAssetEntity pac(final Integer packet_id) { + private static HsHostingAssetRawEntity pac(final Integer packet_id) { return packet_id != null ? hostingAssets.get(PACKET_ID_OFFSET + packet_id) : null; } -- 2.39.5 From cea409c13671c7f45576065cca4182343609171c Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 31 Jul 2024 13:27:13 +0200 Subject: [PATCH 08/14] split persist-stages --- .../hs/migration/ImportHostingAssets.java | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 4c152eb5..60855f6f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -378,30 +378,81 @@ public class ImportHostingAssets extends ImportOfficeData { @Test @Order(19000) @Commit - void persistHostingAssetEntities() { + void persistBookingProjects() { - System.out.println("PERSISTING hosting-assets to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); + System.out.println("PERSISTING booking-projects to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); jpaAttempt.transacted(() -> { context(rbacSuperuser); bookingProjects.forEach(this::persist); }).assertSuccessful(); + } + + @Test + @Order(19010) + @Commit + void persistBookingItems() { + + System.out.println("PERSISTING booking-items to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); jpaAttempt.transacted(() -> { context(rbacSuperuser); bookingItems.forEach(this::persistRecursively); }).assertSuccessful(); + } + + @Test + @Order(19120) + @Commit + void persistCloudServers() { + + System.out.println("PERSISTING cloud-servers to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); persistHostingAssetsOfType(CLOUD_SERVER); + } + + @Test + @Order(19130) + @Commit + void persistManagedServers() { + System.out.println("PERSISTING managed-servers to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); persistHostingAssetsOfType(MANAGED_SERVER); + } + + @Test + @Order(19140) + @Commit + void persistManagedWebspaces() { + System.out.println("PERSISTING managed-webspaces to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); persistHostingAssetsOfType(MANAGED_WEBSPACE); + } + + @Test + @Order(19150) + @Commit + void persistIPNumbers() { + System.out.println("PERSISTING ip-numbers to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); persistHostingAssetsOfType(IPV4_NUMBER); + } + + @Test + @Order(19160) + @Commit + void persistUnixUsers() { + System.out.println("PERSISTING unix-users to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); persistHostingAssetsOfType(UNIX_USER); + } + + @Test + @Order(19170) + @Commit + void persistEmailAliases() { + System.out.println("PERSISTING email-aliases to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); persistHostingAssetsOfType(EMAIL_ALIAS); } @Test - @Order(19010) + @Order(19900) void verifyPersistedUnixUsersWithUserId() { assumeThatWeAreImportingControlledTestData(); -- 2.39.5 From f9ed2bf0de9c1000d26a9554e58e1e80071b9067 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 31 Jul 2024 13:27:28 +0200 Subject: [PATCH 09/14] add performance analysis query --- doc/rbac-performance-analysis.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/doc/rbac-performance-analysis.md b/doc/rbac-performance-analysis.md index b3a578b7..0e5fce45 100644 --- a/doc/rbac-performance-analysis.md +++ b/doc/rbac-performance-analysis.md @@ -111,7 +111,23 @@ time gw-importHostingAssets ### Fetch the Query Statistics -And afterward we can query the statistics in PostgreSQL: +And afterward we can query the statistics in PostgreSQL, e.g.: + +```SQL +WITH statements AS ( + SELECT * FROM pg_stat_statements pss +) +SELECT calls, + total_exec_time::int/(60*1000) as total_exec_time_mins, + mean_exec_time::int as mean_exec_time_millis, + query +FROM statements +WHERE calls > 100 AND shared_blks_hit > 0 +ORDER BY total_exec_time_mins DESC +LIMIT 16; +``` + +### Reset the Query Statistics ```SQL SELECT pg_stat_statements_reset(); -- 2.39.5 From 9a05cad38e6f79fe7329161ab1aa69bcd8010aee Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 1 Aug 2024 09:04:03 +0200 Subject: [PATCH 10/14] use persistViaSql vor HostingAssets and verify number of actually persisted bookingitems + hostingassets --- .../hsadminng/hs/migration/CsvDataImport.java | 77 ++++++++++++++++--- .../hs/migration/ImportHostingAssets.java | 20 +++-- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java index b0f77302..67f42575 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -4,6 +4,7 @@ import com.opencsv.CSVParserBuilder; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; import lombok.SneakyThrows; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; @@ -35,6 +36,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.UUID; import java.util.stream.Collectors; import static java.lang.Boolean.parseBoolean; @@ -122,7 +124,9 @@ public class CsvDataImport extends ContextBasedTest { @SneakyThrows public static String[] parseCsvLine(final String csvLine) { try (final var reader = new CSVReader(new StringReader(csvLine))) { - return ofNullable(reader.readNext()).orElse(emptyArray(String.class)); + return stream(ofNullable(reader.readNext()).orElse(emptyArray(String.class))) + .map(String::trim) + .toArray(String[]::new); } } @@ -138,16 +142,11 @@ public class CsvDataImport extends ContextBasedTest { public T persist(final Integer id, final T entity) { try { - final var asString = entity.toString(); - if ( asString.contains("'null null, null'") || asString.equals("person()")) { - errors.add("skipping to persist empty record-id " + id + " #" + entity.hashCode() + ": " + entity); - return entity; + if (entity instanceof HsHostingAssetRawEntity ha ) { // && ha.getType() == HsHostingAssetType.EMAIL_ALIAS) { + //noinspection unchecked + return (T) persistViaSql(id, ha); } - //System.out.println("persisting #" + entity.hashCode() + ": " + entity); - em.persist(entity); - // uncomment for debugging purposes - // em.flush(); // makes it slow, but produces better error messages - // System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); + return persistViaEM(id, entity); } catch (Exception exc) { errors.add("failed to persist #" + entity.hashCode() + ": " + entity); errors.add(exc.toString()); @@ -155,6 +154,62 @@ public class CsvDataImport extends ContextBasedTest { return entity; } + public T persistViaEM(final Integer id, final T entity) { + //System.out.println("persisting #" + entity.hashCode() + ": " + entity); + em.persist(entity); + // uncomment for debugging purposes + // em.flush(); // makes it slow, but produces better error messages + // System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); + return entity; + } + + @SneakyThrows + public RbacObject persistViaSql(final Integer id, final HsHostingAssetRawEntity entity) { + if (entity.getUuid() == null) { + entity.setUuid(UUID.randomUUID()); + } + + final var query = em.createNativeQuery(""" + insert into hs_hosting_asset( + uuid, + type, + bookingitemuuid, + parentassetuuid, + assignedtoassetuuid, + alarmcontactuuid, + identifier, + caption, + config, + version) + values ( + :uuid, + :type, + :bookingitemuuid, + :parentassetuuid, + :assignedtoassetuuid, + :alarmcontactuuid, + :identifier, + :caption, + cast(:config as jsonb), + :version) + """) + .setParameter("uuid", entity.getUuid()) + .setParameter("type", entity.getType().name()) + .setParameter("bookingitemuuid", ofNullable(entity.getBookingItem()).map(RbacObject::getUuid).orElse(null)) + .setParameter("parentassetuuid", ofNullable(entity.getParentAsset()).map(RbacObject::getUuid).orElse(null)) + .setParameter("assignedtoassetuuid", ofNullable(entity.getAssignedToAsset()).map(RbacObject::getUuid).orElse(null)) + .setParameter("alarmcontactuuid", ofNullable(entity.getAlarmContact()).map(RbacObject::getUuid).orElse(null)) + .setParameter("identifier", entity.getIdentifier()) + .setParameter("caption", entity.getCaption()) + .setParameter("config", entity.getConfig().toString()) + .setParameter("version", entity.getVersion()); + logError(() -> { + final var count = query.executeUpdate(); + assertThat(count).isEqualTo(1); + }); + return entity; + } + protected String toFormattedString(final Map map) { if ( map.isEmpty() ) { return "{}"; @@ -287,7 +342,7 @@ class Record { } String getString(final String columnName) { - return row[columns.indexOf(columnName)]; + return row[columns.indexOf(columnName)].trim(); } boolean isEmpty(final String columnName) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 60855f6f..3d03ab3a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -132,7 +132,6 @@ public class ImportHostingAssets extends ImportOfficeData { void verifyIpNumbers() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null values assertThat(firstOfEachType(5, IPV4_NUMBER)).isEqualToIgnoringWhitespace(""" { 1000363=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.34), @@ -160,7 +159,6 @@ public class ImportHostingAssets extends ImportOfficeData { void verifyHives() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null values assertThat(toFormattedString(first(5, hives))).isEqualToIgnoringWhitespace(""" { 2000001=Hive[hive_id=1, hive_name=h00, inet_addr_id=358, serverRef=null], @@ -232,7 +230,6 @@ public class ImportHostingAssets extends ImportOfficeData { void verifyPacketComponents() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null values assertThat(firstOfEachType(5, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)) .isEqualToIgnoringWhitespace(""" { @@ -283,7 +280,6 @@ public class ImportHostingAssets extends ImportOfficeData { void verifyUnixUsers() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null values assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { 4005803=HsHostingAssetRawEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), @@ -320,7 +316,6 @@ public class ImportHostingAssets extends ImportOfficeData { void verifyEmailAliases() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null values assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace(""" { 5002403=HsHostingAssetRawEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), @@ -456,7 +451,6 @@ public class ImportHostingAssets extends ImportOfficeData { void verifyPersistedUnixUsersWithUserId() { assumeThatWeAreImportingControlledTestData(); - // no contacts yet => mostly null value assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" { 4005803=HsHostingAssetRawEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102090}), @@ -477,6 +471,20 @@ public class ImportHostingAssets extends ImportOfficeData { """); } + @Test + @Order(19910) + void verifyBookingItemsAreActuallyPersisted() { + final var biCount = (Integer) em.createNativeQuery("SELECT count(*) FROM hs_booking_item", Integer.class).getSingleResult(); + assertThat(biCount).isGreaterThan(isImportingControlledTestData() ? 5 : 500); + } + + @Test + @Order(19920) + void verifyHostingAssetsAreActuallyPersisted() { + final var haCount = (Integer) em.createNativeQuery("SELECT count(*) FROM hs_hosting_asset", Integer.class).getSingleResult(); + assertThat(haCount).isGreaterThan(isImportingControlledTestData() ? 20 : 10000); + } + // ============================================================================================ @Test -- 2.39.5 From c1a49d198f9496f8b7f5132eb9c37a16298850d8 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 1 Aug 2024 09:48:13 +0200 Subject: [PATCH 11/14] improved rbac-performance-analysis.md documentation and some cleanup --- doc/rbac-performance-analysis.md | 51 +++++++++++++++++-- .../hs/hosting/asset/HsHostingAsset.java | 3 ++ .../hsadminng/hs/migration/CsvDataImport.java | 7 +-- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/doc/rbac-performance-analysis.md b/doc/rbac-performance-analysis.md index 0e5fce45..1d401886 100644 --- a/doc/rbac-performance-analysis.md +++ b/doc/rbac-performance-analysis.md @@ -1,13 +1,16 @@ # RBAC Performance Analysis -This describes the analysis of the legacy-data-import which took way too long, which turned out to be a problem in the RBAC-access-rights-check. +This describes the analysis of the legacy-data-import which took way too long, which turned out to be a problem in the RBAC-access-rights-check as well as `EntityManager.persist` creating too many SQL queries. ## Our Performance-Problem -During the legacy data import for hosting assets we noticed massive performance problems. The import of about 2200 hosting-assets (IP-numbers, managed-webspaces, managed- and cloud-servers) as well as the creation of booking-items and booking-projects as well as necessary office-data entities (persons, contacts, partners, debitors, relations) **took 10-25 minutes**. +During the legacy data import for hosting assets we noticed massive performance problems. The import of about 2200 hosting-assets (IP-numbers, managed-webspaces, managed- and cloud-servers) as well as the creation of booking-items and booking-projects as well as necessary office-data entities (persons, contacts, partners, debitors, relations) **took 25 minutes**. -We could not find a pattern, why the import mostly took about 25 minutes, but sometimes took *just* 10 minutes. The impression that it had to do with too many other parallel processes, e.g. browser with BBB or IntelliJ IDEA was proved wrong, but stopping all unnecessary processes and performing the import again. +Importing hosting assets up to UnixUsers and EmailAddresses even **took about 100 minutes**. + +(The office data import sometimes, but rarely, took only 10min. +We could not find a pattern, why that was the case. The impression that it had to do with too many other parallel processes, e.g. browser with BBB or IntelliJ IDEA was proved wrong, but stopping all unnecessary processes and performing the import again.) ## Preparation @@ -308,10 +311,48 @@ We changed these mappings from `EAGER` (default) to `LAZY` to `@ManyToOne(fetch Now, finally, the total runtime of the import was down to 12 minutes. This is repeatable, where originally, the import took about 25mins in most cases and just rarely - and for unknown reasons - 10min. +### Importing UnixUser and EmailAlias Assets + +But once UnixUser and EmailAlias assets got added to the import, the total time went up to about 110min. + +This was not acceptable, especially not, considering that domains, email-addresses and database-assets are almost 10 times that number and thus the import would go up to over 1100min which is 20 hours. + +In a first step, a `HsHostingAssetRawEntity` was created, mapped to the raw table (hs_hosting_asset) not to the RBAC-view (hs_hosting_asset_rv). Unfortunately we did not keep measurements, but that was only part of the problem anyway. + +The main problem was, that there is something strange with persisting (`EntityManager.persist`) for EmailAlias assets. Where importing UnixUsers was mostly slow due to RBAC SELECT-permission checks, persisting EmailAliases suddenly created about a million (in numbers 1.000.000) SQL UPDATE statements after the INSERT, all with the same data, just increased version number (used for optimistic locking). We were not able to figure out why this happened. + +Keep in mind, it's the same table with the same RBAC-triggers, just a different value in the type column. + +Once `EntityManager.persist` was replaced by an explicit SQL INSERT - just for `HsHostingAssetRawEntity`, the total time was down to 17min. Thus importing the UnixUsers and EmailAliases took just 5min, which is an acceptable result. The total import of all HostingAssets is now estimated to about 1 hour (on my developer laptop). + + +## Further Options To Explore + +1. Instead of separate SQL INSERT statements, we could try bulk INSERT. +2. We could use the SQL INSERT method for all entity-classes, or at least for all which have high row counts. +3. For the production code, we could use raw-entities for referenced entities, here usually RBAC SELECT permission is given anyway. + + ## Summary -That the import runtime is down to about 12min is repeatable, where originally, the import took about 25mins in most cases and just rarely - and for unknown reasons - just 10min. +### What we did Achieve? + +In a first step, the total import runtime for office entities was reduced from about 25min to about 10min. + +In a second step, we reduced the import of booking- and hosting-assets from about 100min (not counting the required office entities) to 5min. + +### What Helped? Merging the recursive CTE query to determine the RBAC SELECT-permission, made it more clear which business-queries take the time. -Avoiding EAGER-loading where not neccessary, reduced the total runtime of the import to about the half. +Avoiding EAGER-loading where not necessary, reduced the total runtime of the import to about the half. + +The major improvement came from using direct INSERT statements, which then also bypassed the RBAC SELECT permission checks. + +### What Still Has To Be Done? + +Where this performance analysis was mostly helping the performance of the legacy data import, we still need measures and improvements for the productive code. + +For sure, using more LAZY-loading also helps in the production code. For some more ideas see section _Further Options To Explore_. + + diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java index afa3b0e9..637e19cb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java @@ -11,6 +11,7 @@ import net.hostsharing.hsadminng.stringify.Stringifyable; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import static java.util.Collections.emptyMap; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @@ -27,6 +28,8 @@ public interface HsHostingAsset extends Stringifyable, RbacObject T persist(final Integer id, final T entity) { try { - if (entity instanceof HsHostingAssetRawEntity ha ) { // && ha.getType() == HsHostingAssetType.EMAIL_ALIAS) { + if (entity instanceof HsHostingAsset ha) { //noinspection unchecked return (T) persistViaSql(id, ha); } @@ -164,7 +164,7 @@ public class CsvDataImport extends ContextBasedTest { } @SneakyThrows - public RbacObject persistViaSql(final Integer id, final HsHostingAssetRawEntity entity) { + public RbacObject persistViaSql(final Integer id, final HsHostingAsset entity) { if (entity.getUuid() == null) { entity.setUuid(UUID.randomUUID()); } @@ -203,8 +203,9 @@ public class CsvDataImport extends ContextBasedTest { .setParameter("caption", entity.getCaption()) .setParameter("config", entity.getConfig().toString()) .setParameter("version", entity.getVersion()); + + final var count = query.executeUpdate(); logError(() -> { - final var count = query.executeUpdate(); assertThat(count).isEqualTo(1); }); return entity; -- 2.39.5 From a840337a54fc1f4a0141dc983bb186d674fb7ee0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 1 Aug 2024 11:28:57 +0200 Subject: [PATCH 12/14] improve rbac-performance-analysis.md again --- doc/rbac-performance-analysis.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/rbac-performance-analysis.md b/doc/rbac-performance-analysis.md index 1d401886..43f47ec6 100644 --- a/doc/rbac-performance-analysis.md +++ b/doc/rbac-performance-analysis.md @@ -291,6 +291,7 @@ The slowest query now was fetching Relations joined with Contact, Anchor-Person We changed these mappings from `EAGER` (default) to `LAZY` to `@ManyToOne(fetch = FetchType.LAZY)` and got this result: +:::small | query | calls | total (min) | mean (ms) | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|-------------|----------| | select hope1_0.uuid,hope1_0.familyname,hope1_0.givenname,hope1_0.persontype,hope1_0.salutation,hope1_0.title,hope1_0.tradename,hope1_0.version from public.hs_office_person_rv hope1_0 where hope1_0.uuid=$1 | 1015 | 4 | 238 | @@ -325,7 +326,28 @@ Keep in mind, it's the same table with the same RBAC-triggers, just a different Once `EntityManager.persist` was replaced by an explicit SQL INSERT - just for `HsHostingAssetRawEntity`, the total time was down to 17min. Thus importing the UnixUsers and EmailAliases took just 5min, which is an acceptable result. The total import of all HostingAssets is now estimated to about 1 hour (on my developer laptop). +Now, the longest running queries are these: +| No.| calls | total_m | mean_ms | query | +|---:|---------|--------:|--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1 | 13.093 | 4 | 21 | insert into hs_hosting_asset( uuid, type, bookingitemuuid, parentassetuuid, assignedtoassetuuid, alarmcontactuuid, identifier, caption, config, version) values ( $1, $2, $3, $4, $5, $6, $7, $8, cast($9 as jsonb), $10) | +| 2 | 517 | 4 | 502 | select hore1_0.uuid,hore1_0.anchoruuid,hore1_0.contactuuid,hore1_0.holderuuid,hore1_0.mark,hore1_0.type,hore1_0.version from public.hs_office_relation_rv hore1_0 where hore1_0.uuid=$1 | +| 3 | 13.144 | 4 | 21 | call buildRbacSystemForHsHostingAsset(NEW) | +| 4 | 96.632 | 3 | 2 | call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | +| 5 | 120.815 | 3 | 2 | select * from isGranted(array[granteeId], grantedId) | +| 6 | 123.740 | 3 | 2 | with recursive grants as ( select descendantUuid, ascendantUuid from RbacGrants where descendantUuid = grantedId union all select "grant".descendantUuid, "grant".ascendantUuid from RbacGrants "grant" inner join grants recur on recur.ascendantUuid = "grant".descendantUuid ) select exists ( select $3 from grants where ascendantUuid = any(granteeIds) ) or grantedId = any(granteeIds) | +| 7 | 497 | 2 | 259 | select hoce1_0.uuid,hoce1_0.caption,hoce1_0.emailaddresses,hoce1_0.phonenumbers,hoce1_0.postaladdress,hoce1_0.version from public.hs_office_contact_rv hoce1_0 where hoce1_0.uuid=$1 | +| 8 | 497 | 2 | 255 | select hope1_0.uuid,hope1_0.familyname,hope1_0.givenname,hope1_0.persontype,hope1_0.salutation,hope1_0.title,hope1_0.tradename,hope1_0.version from public.hs_office_person_rv hope1_0 where hope1_0.uuid=$1 | +| 9 | 13.144 | 1 | 8 | SELECT createRoleWithGrants( hsHostingAssetTENANT(NEW), permissions => array[$7], incomingSuperRoles => array[ hsHostingAssetAGENT(NEW), hsOfficeContactADMIN(newAlarmContact)], outgoingSubRoles => array[ hsBookingItemTENANT(newBookingItem), hsHostingAssetTENANT(newParentAsset)] ) | +| 10 | 13.144 | 1 | 5 | SELECT createRoleWithGrants( hsHostingAssetADMIN(NEW), permissions => array[$7], incomingSuperRoles => array[ hsBookingItemAGENT(newBookingItem), hsHostingAssetAGENT(newParentAsset), hsHostingAssetOWNER(NEW)] ) | + +That the `INSERT into hs_hosting_asset` (No. 1) takes up the most time, seems to be normal, and 21ms for each call is also fine. + +It seems that the trigger effects (eg. No. 3 and No. 4) are included in the measure for the causing INSERT, otherwise summing up the totals would exceed the actual total time of the whole import. And it was to be expected that building the RBAC rules for new business objects takes most of the time. + +In production, the `SELECT ... FROM hs_office_relation_rv` (No. 2) with about 0.5 seconds could still be a problem. But once we apply the improvements from the hosting asset area also to the office area, this should not be a problem for the import anymore. + + ## Further Options To Explore 1. Instead of separate SQL INSERT statements, we could try bulk INSERT. -- 2.39.5 From 9bd8c7b54e8168c189282219affe09243110d298 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 1 Aug 2024 11:45:37 +0200 Subject: [PATCH 13/14] fixes after code-review --- .../701-hosting-asset/7010-hs-hosting-asset.sql | 4 ++-- .../HsUnixUserHostingAssetValidatorUnitTest.java | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index 48a79a71..2586781e 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -118,8 +118,8 @@ create trigger hs_hosting_asset_type_hierarchy_check_tg CREATE SEQUENCE IF NOT EXISTS hs_hosting_asset_unixuser_system_id_seq AS integer - MINVALUE 100000000 - MAXVALUE 199999999 + MINVALUE 1000000 + MAXVALUE 9999999 NO CYCLE OWNED BY NONE; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java index 7ae081c2..e24eaf51 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java @@ -109,8 +109,8 @@ class HsUnixUserHostingAssetValidatorUnitTest { .identifier("abc00-temp") .caption("some test UnixUser with invalid properties") .config(ofEntries( - entry("SSD hard quota", 100), - entry("SSD soft quota", 200), + entry("SSD hard quota", 60000), + entry("SSD soft quota", 70000), entry("HDD hard quota", 100), entry("HDD soft quota", 200), entry("shell", "/is/invalid"), @@ -126,7 +126,8 @@ class HsUnixUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'UNIX_USER:abc00-temp.config.SSD soft quota' is expected to be at most 100 but is 200", + "'UNIX_USER:abc00-temp.config.SSD hard quota' is expected to be at most 51200 but is 60000", + "'UNIX_USER:abc00-temp.config.SSD soft quota' is expected to be at most 60000 but is 70000", "'UNIX_USER:abc00-temp.config.HDD hard quota' is expected to be at most 0 but is 100", "'UNIX_USER:abc00-temp.config.HDD soft quota' is expected to be at most 100 but is 200", "'UNIX_USER:abc00-temp.config.homedir' is readonly but given as '/is/read-only'", -- 2.39.5 From 98c1dbe9be1644431bfe7bbb582c3802a9915cab Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 1 Aug 2024 12:07:47 +0200 Subject: [PATCH 14/14] allow /dev/null as alias target and remove single quotes --- .../HsEMailAliasHostingAssetValidator.java | 3 +- ...ailAliasHostingAssetValidatorUnitTest.java | 28 ++++++++++++++----- .../hsadminng/hs/migration/CsvDataImport.java | 2 +- .../hs/migration/ImportHostingAssets.java | 9 ++---- .../migration/hosting/emailalias.csv | 1 + 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java index ab0cb77c..f6c412bb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java @@ -14,6 +14,7 @@ class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator { private static final String EMAIL_ADDRESS_REGEX = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"; // RFC 5322 private static final String INCLUDE_REGEX = "^:include:/.*$"; private static final String PIPE_REGEX = "^\\|.*$"; + private static final String DEV_NULL_REGEX = "^/dev/null$"; public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322 HsEMailAliasHostingAssetValidator() { @@ -21,7 +22,7 @@ class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator { AlarmContact.isOptional(), arrayOf( - stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_REGEX, INCLUDE_REGEX, PIPE_REGEX) + stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_REGEX, INCLUDE_REGEX, PIPE_REGEX, DEV_NULL_REGEX) ).required().minLength(1)); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java index 9d9422ab..06237102 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java @@ -22,18 +22,24 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( - "{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.-]+$, ^:include:/.*$, ^\\|.*$], 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.-]+$, ^:include:/.*$, ^\\|.*$, ^/dev/null$], maxLength=320}, required=true, minLength=1}"); } @Test - void validatesValidEntity() { + void acceptsValidEntity() { // given final var emailAliasHostingAssetEntity = HsHostingAssetEntity.builder() .type(EMAIL_ALIAS) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .identifier("xyz00-office") .config(Map.ofEntries( - entry("target", Array.of("xyz00", "xyz00-abc", "office@example.com")) + entry("target", Array.of( + "xyz00", + "xyz00-abc", + "office@example.com", + "/dev/null", + "|/home/pacs/xyz00/mailinglists/ecartis -s xyz00-intern" + )) )) .build(); final var validator = HostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType()); @@ -46,14 +52,22 @@ class HsEMailAliasHostingAssetValidatorUnitTest { } @Test - void validatesProperties() { + void rejectsInvalidConfig() { // given final var emailAliasHostingAssetEntity = HsHostingAssetEntity.builder() .type(EMAIL_ALIAS) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .identifier("xyz00-office") .config(Map.ofEntries( - entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com")) + entry("target", Array.of( + "/dev/null", + "xyz00", + "xyz00-abc", + "garbage", + "office@example.com", + ":include:/home/pacs/xyz00/mailinglists/textfile", + "|/home/pacs/xyz00/mailinglists/executable" + )) )) .build(); final var validator = HostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType()); @@ -63,11 +77,11 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'EMAIL_ALIAS:xyz00-office.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.-]+$, ^:include:/.*$, ^\\|.*$] but 'garbage' does not match any"); + "'EMAIL_ALIAS:xyz00-office.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.-]+$, ^:include:/.*$, ^\\|.*$, ^/dev/null$] but 'garbage' does not match any"); } @Test - void validatesInvalidIdentifier() { + void rejectsInvalidIndentifier() { // given final var emailAliasHostingAssetEntity = HsHostingAssetEntity.builder() .type(EMAIL_ALIAS) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java index e1dd13c5..6405d543 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -126,11 +126,11 @@ public class CsvDataImport extends ContextBasedTest { try (final var reader = new CSVReader(new StringReader(csvLine))) { return stream(ofNullable(reader.readNext()).orElse(emptyArray(String.class))) .map(String::trim) + .map(target -> target.startsWith("'") && target.endsWith("'") ? target.substring(1, target.length()-1) : target) .toArray(String[]::new); } } - String[] trimAll(final String[] record) { for (int i = 0; i < record.length; ++i) { if (record[i] != null) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 3d03ab3a..3092dd85 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -327,7 +327,8 @@ public class ImportHostingAssets extends ImportOfficeData { 5002452=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { "target": "[]"}), 5002453=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]"}), 5002454=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { "target": "[/dev/null]"}), - 5002455=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"}) + 5002455=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"}), + 5002456=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-1_with_single_quotes, mim00-1_with_single_quotes, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/rir00/mailinglist/ecartis -r kybs06-intern]"}) } """); } @@ -500,11 +501,7 @@ public class ImportHostingAssets extends ImportOfficeData { validation failed for id:5002453( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]" } - )): ['EMAIL_ALIAS:mim00-0_entries.config.target' length is expected to be at min 1 but length of [[]] is 0]""", - """ - validation failed for id:5002454( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { - "target": "[/dev/null]" - })): ['EMAIL_ALIAS:mim00-dev.null.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.-]+$,^:include:/.*$,^\\|.*$] but '/dev/null' does not match any]""" + )): ['EMAIL_ALIAS:mim00-0_entries.config.target' length is expected to be at min 1 but length of [[]] is 0]""" ); } else { super.logErrors(); diff --git a/src/test/resources/migration/hosting/emailalias.csv b/src/test/resources/migration/hosting/emailalias.csv index 7d5cd887..6b007ce3 100644 --- a/src/test/resources/migration/hosting/emailalias.csv +++ b/src/test/resources/migration/hosting/emailalias.csv @@ -9,3 +9,4 @@ emailalias_id;pac_id;name;target 2453;1112;mim00-0_entries;"" 2454;1112;mim00-dev.null; /dev/null 2455;1112;mim00-1_with_space;" ""|/home/pacs/mim00/install/corpslistar/listar""" +2456;1112;mim00-1_with_single_quotes;'|/home/pacs/rir00/mailinglist/ecartis -r kybs06-intern' -- 2.39.5