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