From 3d00696985101ede861c304666e0df5b31a8dff6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 1 Aug 2024 19:03:30 +0200 Subject: [PATCH 1/5] importing database-users and -databases and creating database-instances (WIP) --- .../hsadminng/hash/HashGenerator.java | 10 + .../hs/hosting/asset/HsHostingAssetType.java | 1 + ...sMariaDbDatabaseHostingAssetValidator.java | 4 +- .../HsMariaDbUserHostingAssetValidator.java | 4 +- ...stgreSqlDatabaseHostingAssetValidator.java | 4 +- ...HsPostgreSqlUserHostingAssetValidator.java | 12 +- .../hs/validation/HsEntityValidator.java | 14 +- .../hs/validation/PasswordProperty.java | 6 +- .../hs/validation/StringProperty.java | 8 + .../changelog/9-hs-global/9000-statistics.sql | 22 ++ .../db/changelog/db.changelog-master.yaml | 2 + ...DatabaseHostingAssetValidatorUnitTest.java | 8 +- ...iaDbUserHostingAssetValidatorUnitTest.java | 10 +- ...DatabaseHostingAssetValidatorUnitTest.java | 14 +- ...eSqlUserHostingAssetValidatorUnitTest.java | 10 +- .../hsadminng/hs/migration/CsvDataImport.java | 33 ++- .../hs/migration/ImportHostingAssets.java | 197 +++++++++++++++++- .../resources/migration/hosting/database.csv | 23 ++ .../migration/hosting/database_user.csv | 15 ++ 19 files changed, 362 insertions(+), 35 deletions(-) create mode 100644 src/main/resources/db/changelog/9-hs-global/9000-statistics.sql create mode 100644 src/test/resources/migration/hosting/database.csv create mode 100644 src/test/resources/migration/hosting/database_user.csv diff --git a/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java b/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java index 5bc09cc6..a577f3ce 100644 --- a/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java @@ -59,6 +59,10 @@ public final class HashGenerator { this.algorithm = algorithm; } + public boolean couldBeHash(final String value) { + return value.startsWith(algorithm.prefix); + } + public String hash(final String plaintextPassword) { if (plaintextPassword == null) { throw new IllegalStateException("no password given"); @@ -67,6 +71,12 @@ public final class HashGenerator { return algorithm.implementation.apply(this, plaintextPassword); } + public String hashIfNotYetHashed(final String plaintextPasswordOrHash) { + return couldBeHash(plaintextPasswordOrHash) + ? plaintextPasswordOrHash + : hash(plaintextPasswordOrHash); + } + public static void nextSalt(final String salt) { predefinedSalts.add(salt); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java index 4209a05e..f08248c4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java @@ -50,6 +50,7 @@ public enum HsHostingAssetType implements Node { inGroup("Webspace"), requiredParent(MANAGED_WEBSPACE)), + // TODO.spec: do we really want to keep email aliases or migrate to unix users with .forward? EMAIL_ALIAS( // named e.g. xyz00-abc inGroup("Webspace"), requiredParent(MANAGED_WEBSPACE)), 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 48618be3..823308ed 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 @@ -9,6 +9,8 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator { + final static String HEAD_REGEXP = "^MAD\\|"; + public HsMariaDbDatabaseHostingAssetValidator() { super( MARIADB_DATABASE, @@ -20,6 +22,6 @@ class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator @Override protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); + return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$"); } } 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 15ae0b45..58a33520 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 @@ -10,6 +10,8 @@ import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordP class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator { + final static String HEAD_REGEXP = "^MAU\\|"; + public HsMariaDbUserHostingAssetValidator() { super( MARIADB_USER, @@ -28,6 +30,6 @@ class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator { @Override protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); + return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-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 57d302d0..830b2fbf 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 @@ -9,6 +9,8 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValidator { + final static String HEAD_REGEXP = "^PGD\\|"; + public HsPostgreSqlDatabaseHostingAssetValidator() { super( PGSQL_DATABASE, @@ -23,6 +25,6 @@ class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValida @Override protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); + return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$"); } } 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 7d527892..736648ff 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 @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; +import java.util.List; import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_USER; @@ -10,6 +11,8 @@ import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordP class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator { + final static String HEAD_REGEXP = "^PGU\\|"; + public HsPostgreSqlUserHostingAssetValidator() { super( PGSQL_USER, @@ -25,9 +28,16 @@ class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.SCRAM_SHA256).writeOnly()); } + // FIXME: remove method + @Override + public List validateEntity(final HsHostingAsset assetEntity) { + final var result = super.validateEntity(assetEntity); + return result; + } + @Override protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); - return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); + return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$"); } } 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 77cc2514..0ba440d2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -5,6 +5,7 @@ package net.hostsharing.hsadminng.hs.validation; import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -62,7 +63,18 @@ public abstract class HsEntityValidator { } protected ArrayList validateProperties(final PropertiesProvider propsProvider) { - final var result = new ArrayList(); + final var result = new ArrayList() { + + @Override + public boolean add(final String s) { + return super.add(s); + } + + @Override + public boolean addAll(final Collection c) { + return super.addAll(c); + } + }; // verify that all actually given properties are specified final var properties = propsProvider.directProps(); 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 083e69ca..fa23b1d2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java @@ -31,6 +31,10 @@ public class PasswordProperty extends StringProperty { @Override protected void validate(final List result, final String propValue, final PropertiesProvider propProvider) { + if (HashGenerator.using(hashedUsing).couldBeHash(propValue) && propValue.length() > this.maxLength()) { + // already hashed => do not validate + return; + } super.validate(result, propValue, propProvider); validatePassword(result, propValue); } @@ -40,7 +44,7 @@ public class PasswordProperty extends StringProperty { computedBy( ComputeMode.IN_PREP, (em, entity) -> ofNullable(entity.getDirectValue(propertyName, String.class)) - .map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password)) + .map(password -> HashGenerator.using(algorithm).withRandomSalt().hashIfNotYetHashed(password)) .orElse(null)); return self(); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/StringProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/StringProperty.java index aa804916..7870ca87 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/StringProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/StringProperty.java @@ -41,11 +41,19 @@ public class StringProperty

> extends ValidatableProp return self(); } + public Integer minLength() { + return this.minLength; + } + public P maxLength(final int maxLength) { this.maxLength = maxLength; return self(); } + public Integer maxLength() { + return this.maxLength; + } + public P matchesRegEx(final String... regExPattern) { this.matchesRegEx = stream(regExPattern).map(Pattern::compile).toArray(Pattern[]::new); return self(); diff --git a/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql b/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql new file mode 100644 index 00000000..f4c841e9 --- /dev/null +++ b/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql @@ -0,0 +1,22 @@ +--liquibase formatted sql + +-- ============================================================================ +--changeset hs-global-object-statistics:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +select * + from (select count, "table" as "rbac-table", '' as "hs-table", '' as "type" + from rbacstatisticsview + union all + select to_char(count(*)::int, '9 999 999 999') as "count", 'objects' as "rbac-table", objecttable as "hs-table", '' as "type" + from rbacobject + group by objecttable + union all + select to_char(count(*)::int, '9 999 999 999'), 'objects', 'hs_hosting_asset', type::text + from hs_hosting_asset + group by type + union all + select to_char(count(*)::int, '9 999 999 999'), 'objects', 'hs_booking_item', type::text + from hs_booking_item + group by type + ) as totals order by replace(count, ' ', '')::int desc; +--// diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index a9c6711d..8771ae81 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -151,3 +151,5 @@ databaseChangeLog: file: db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql - include: file: db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql + - include: + file: db/changelog/9-hs-global/9000-statistics.sql diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidatorUnitTest.java index 37c8fb85..7e7c8b5b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidatorUnitTest.java @@ -40,7 +40,7 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { return HsHostingAssetEntity.builder() .type(MARIADB_DATABASE) .parentAsset(GIVEN_MARIADB_USER) - .identifier("xyz00_temp") + .identifier("MAD|xyz00_temp") .caption("some valid test MariaDB-Database") .config(new HashMap<>(ofEntries( entry("encoding", "latin1") @@ -93,8 +93,8 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'MARIADB_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", - "'MARIADB_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" + "'MARIADB_DATABASE:MAD|xyz00_temp.config.unknown' is not expected but is set to 'wrong'", + "'MARIADB_DATABASE:MAD|xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" ); } @@ -111,6 +111,6 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'"); + "'identifier' expected to match '^MAD\\|xyz00$|^MAD\\|xyz00_[a-zA-Z0-9_]+$', but is 'xyz99-temp'"); } } 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 97c8429b..70b823c8 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 @@ -32,7 +32,7 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { .type(MARIADB_USER) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .assignedToAsset(GIVEN_MARIADB_INSTANCE) - .identifier("xyz00_temp") + .identifier("MAU|xyz00_temp") .caption("some valid test MariaDB-User") .config(new HashMap<>(ofEntries( entry("password", "Test1234") @@ -101,9 +101,9 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'MARIADB_USER:xyz00_temp.config.unknown' is not expected but is set to '100'", - "'MARIADB_USER:xyz00_temp.config.password' length is expected to be at min 8 but length of provided value is 5", - "'MARIADB_USER:xyz00_temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters" + "'MARIADB_USER:MAU|xyz00_temp.config.unknown' is not expected but is set to '100'", + "'MARIADB_USER:MAU|xyz00_temp.config.password' length is expected to be at min 8 but length of provided value is 5", + "'MARIADB_USER:MAU|xyz00_temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters" ); } @@ -120,6 +120,6 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'"); + "'identifier' expected to match '^MAU\\|xyz00$|^MAU\\|xyz00_[a-zA-Z0-9_]+$', but is 'xyz99-temp'"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java index 35780466..78a59288 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java @@ -42,7 +42,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { return HsHostingAssetEntity.builder() .type(PGSQL_DATABASE) .parentAsset(GIVEN_PGSQL_USER) - .identifier("xyz00_db") + .identifier("PGD|xyz00_db") .caption("some valid test PgSql-Database") .config(new HashMap<>(ofEntries( entry("encoding", "LATIN1") @@ -94,9 +94,9 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_DATABASE:xyz00_db.bookingItem' must be null but is of type CLOUD_SERVER", - "'PGSQL_DATABASE:xyz00_db.parentAsset' must be of type PGSQL_USER but is of type PGSQL_INSTANCE", - "'PGSQL_DATABASE:xyz00_db.assignedToAsset' must be null but is of type PGSQL_INSTANCE" + "'PGSQL_DATABASE:PGD|xyz00_db.bookingItem' must be null but is of type CLOUD_SERVER", + "'PGSQL_DATABASE:PGD|xyz00_db.parentAsset' must be of type PGSQL_USER but is of type PGSQL_INSTANCE", + "'PGSQL_DATABASE:PGD|xyz00_db.assignedToAsset' must be null but is of type PGSQL_INSTANCE" ); } @@ -116,8 +116,8 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_DATABASE:xyz00_db.config.unknown' is not expected but is set to 'wrong'", - "'PGSQL_DATABASE:xyz00_db.config.encoding' is expected to be of type String, but is of type Integer" + "'PGSQL_DATABASE:PGD|xyz00_db.config.unknown' is not expected but is set to 'wrong'", + "'PGSQL_DATABASE:PGD|xyz00_db.config.encoding' is expected to be of type String, but is of type Integer" ); } @@ -134,6 +134,6 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'"); + "'identifier' expected to match '^PGD\\|xyz00$|^PGD\\|xyz00_[a-zA-Z0-9_]+$', but is 'xyz99-temp'"); } } 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 588631c2..bb589a7b 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 @@ -35,7 +35,7 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest { .type(PGSQL_USER) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .assignedToAsset(GIVEN_PGSQL_INSTANCE) - .identifier("xyz00_temp") + .identifier("PGU|xyz00_temp") .caption("some valid test PgSql-User") .config(new HashMap<>(ofEntries( entry("password", "Test1234") @@ -104,9 +104,9 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_USER:xyz00_temp.config.unknown' is not expected but is set to '100'", - "'PGSQL_USER:xyz00_temp.config.password' length is expected to be at min 8 but length of provided value is 5", - "'PGSQL_USER:xyz00_temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters" + "'PGSQL_USER:PGU|xyz00_temp.config.unknown' is not expected but is set to '100'", + "'PGSQL_USER:PGU|xyz00_temp.config.password' length is expected to be at min 8 but length of provided value is 5", + "'PGSQL_USER:PGU|xyz00_temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters" ); } @@ -123,6 +123,6 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'"); + "'identifier' expected to match '^PGU\\|xyz00$|^PGU\\|xyz00_[a-zA-Z0-9_]+$', but is 'xyz99-temp'"); } } 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 6405d543..70337bcb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -48,6 +48,7 @@ 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; +import static org.junit.jupiter.api.Assertions.fail; public class CsvDataImport extends ContextBasedTest { @@ -281,6 +282,12 @@ public class CsvDataImport extends ContextBasedTest { }).assertSuccessful(); } + // makes it possible to fail when an expression is expected + T failWith(final String message) { + fail(message); + return null; + } + void logError(final Runnable assertion) { try { assertion.run(); @@ -290,7 +297,9 @@ public class CsvDataImport extends ContextBasedTest { } void logErrors() { - assertThat(errors).isEmpty(); + final var errorsToLog = new ArrayList<>(errors); + errors.clear(); + assertThat(errorsToLog).isEmpty(); } void expectErrors(final String... expectedErrors) { @@ -305,8 +314,16 @@ public class CsvDataImport extends ContextBasedTest { } 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); + final var sortedExpected = expected.stream() + .map(m -> m.replaceAll("\\s+", " ")) + .map(m -> m.replaceAll("^ ", "")) + .map(m -> m.replaceAll(" $", "")) + .toList(); + final var sortedActual = actual.stream() + .map(m -> m.replaceAll("\\s+", " ")) + .map(m -> m.replaceAll("^ ", "")) + .map(m -> m.replaceAll(" $", "")) + .toArray(String[]::new); assertThat(sortedExpected).containsExactlyInAnyOrder(sortedActual); } @@ -324,6 +341,10 @@ class Columns { } int indexOf(final String columnName) { + return columnNames.indexOf(columnName); + } + + int indexOfOrFail(final String columnName) { int index = columnNames.indexOf(columnName); if (index < 0) { throw new RuntimeException("column name '" + columnName + "' not found in: " + columnNames); @@ -342,6 +363,12 @@ class Record { this.row = row; } + String getString(final String columnName, final String defaultValue) { + final var index = columns.indexOf(columnName); + final var value = index >= 0 && index < row.length ? row[index].trim() : null; + return value != null ? value : defaultValue; + } + String getString(final String columnName) { return row[columns.indexOf(columnName)].trim(); } 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 3092dd85..17943479 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.migration; import net.hostsharing.hsadminng.context.Context; +import net.hostsharing.hsadminng.hash.HashGenerator; +import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm; import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; @@ -31,13 +33,21 @@ import java.util.function.Function; import static java.util.Arrays.stream; import static java.util.Map.entry; +import static java.util.Map.ofEntries; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; 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; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_DATABASE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_DATABASE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_INSTANCE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_USER; 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; @@ -97,6 +107,8 @@ public class ImportHostingAssets extends ImportOfficeData { static final Integer PACKET_ID_OFFSET = 3000000; static final Integer UNIXUSER_ID_OFFSET = 4000000; static final Integer EMAILALIAS_ID_OFFSET = 5000000; + static final Integer DBUSER_ID_OFFSET = 6000000; + static final Integer DB_ID_OFFSET = 7000000; record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference serverRef) {} @@ -333,6 +345,87 @@ public class ImportHostingAssets extends ImportOfficeData { """); } + @Test + @Order(15000) + void createDatabaseInstances() { + // FIXME + } + + @Test + @Order(15009) + void verifyDatabaseINSTANCES() { + assumeThatWeAreImportingControlledTestData(); +// FIXME +// assertThat(firstOfEachType(5, PGSQL_INSTANCE, MARIADB_INSTANCE)).isEqualToIgnoringWhitespace(""" +// { +// } +// """); + } + + @Test + @Order(15010) + void importDatabaseUsers() { + try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/database_user.csv")) { + final var lines = readAllLines(reader); + importDatabaseUsers(justHeader(lines), withoutHeader(lines)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + @Order(15019) + void verifyDatabaseUsers() { + assumeThatWeAreImportingControlledTestData(); + + assertThat(firstOfEachType(5, PGSQL_USER, MARIADB_USER)).isEqualToIgnoringWhitespace(""" + { + 6001858=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00, hsh00, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:PGI|hsh00, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$JDiZmaxU+O+ByArLY/CkYZ8HbOk0r/I8LyABnno5gQs=:NI3T500/63dzI1B07Jh3UtQGlukS6JxuS0XoxM/QgAc="}), + 6001860=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_hsadmin, hsh00_hsadmin, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:PGI|hsh00_hsadmin, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$54Wh+OGx/GaIvAia+I3k78jHGhqmYwe4+iLssmH5zhk=:D4Gq1z2Li2BVSaZrz1azDrs6pwsIzhq4+suK1Hh6ZIg="}), + 6001861=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_hsadmin_ro, hsh00_hsadmin_ro, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:PGI|hsh00_hsadmin_ro, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$UhJnJJhmKANbcaG+izWK3rz5bmhhluSuiCJFlUmDVI8=:6AC4mbLfJGiGlEOWhpz9BivvMODhLLHOnRnnktJPgn8="}), + 6004908=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_mantis, hsh00_mantis, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_mantis, { "password": "*EA4C0889A22AAE66BBEBC88161E8CF862D73B44F"}), + 6004909=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_mantis_ro, hsh00_mantis_ro, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_mantis_ro, { "password": "*B3BB6D0DA2EC01958616E9B3BCD2926FE8C38383"}), + 6004931=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_phpPgSqlAdmin, hsh00_phpPgSqlAdmin, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_phpPgSqlAdmin, { "password": "*59067A36BA197AD0A47D74909296C5B002A0FB9F"}), + 6004932=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_phpMyAdmin, hsh00_phpMyAdmin, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_phpMyAdmin, { "password": "*3188720B1889EF5447C722629765F296F40257C2"}), + 6007520=HsHostingAssetRawEntity(MARIADB_USER, MAU|lug00_wla, lug00_wla, MANAGED_WEBSPACE:lug00, MARIADB_INSTANCE:MAI|lug00_wla, { "password": "*11667C0EAC42BF8B0295ABEDC7D2868A835E4DB5"}), + 6007522=HsHostingAssetRawEntity(PGSQL_USER, PGU|lug00_ola, lug00_ola, MANAGED_WEBSPACE:lug00, PGSQL_INSTANCE:PGI|lug00_ola, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$tir+cV3ZzOZeEWurwAJk+8qkvsTAWaBfwx846oYMOr4=:p4yk/4hHkfSMAFxSuTuh3RIrbSpHNBh7h6raVa3nt1c="}), + 6007605=HsHostingAssetRawEntity(PGSQL_USER, PGU|mim00_office, mim00_office, MANAGED_WEBSPACE:mim00, PGSQL_INSTANCE:PGI|mim00_office, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$43jziwd1o+nkfjE0zFbks24Zy5GK+km87B7vzEQt4So=:xRQntZxBxdo1JJbhkegnUFKHT0T8MDW75hkQs2S3z6k="}) + } + """); + } + + @Test + @Order(15020) + void importDatabases() { + try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/database.csv")) { + final var lines = readAllLines(reader); + importDatabases(justHeader(lines), withoutHeader(lines)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + @Order(15029) + void verifyDatabases() { + assumeThatWeAreImportingControlledTestData(); + + assertThat(firstOfEachType(5, PGSQL_DATABASE, MARIADB_DATABASE)).isEqualToIgnoringWhitespace(""" + { + 7000077=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_vorstand, hsh00_vorstand, { "encoding": "LATIN1"}), + 7000786=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_addr, hsh00_addr, { "encoding": "latin1"}), + 7000805=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_db2, hsh00_db2, { "encoding": "latin1"}), + 7001858=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00, hsh00, { "encoding": "LATIN1"}), + 7001860=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_hsadmin, hsh00_hsadmin, { "encoding": "UTF8"}), + 7004908=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_mantis, hsh00_mantis, { "encoding": "utf8"}), + 7004931=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_phpPgSqlAdmin, hsh00_phpPgSqlAdmin, { "encoding": "UTF8"}), + 7004932=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_phpPgSqlAdmin_new, hsh00_phpPgSqlAdmin_new, { "encoding": "UTF8"}), + 7004941=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_phpMyAdmin, hsh00_phpMyAdmin, { "encoding": "utf8"}), + 7004942=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_phpMyAdmin_old, hsh00_phpMyAdmin_old, { "encoding": "utf8"}) + } + """); + } + // -------------------------------------------------------------------------------------------- @Test @@ -447,6 +540,30 @@ public class ImportHostingAssets extends ImportOfficeData { persistHostingAssetsOfType(EMAIL_ALIAS); } + @Test + @Order(19200) + @Commit + void persistDatabaseInstances() { + System.out.println("PERSISTING db-users to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); + persistHostingAssetsOfType(PGSQL_INSTANCE, MARIADB_INSTANCE); + } + + @Test + @Order(19210) + @Commit + void persistDatabaseUsers() { + System.out.println("PERSISTING db-users to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); + persistHostingAssetsOfType(PGSQL_USER, MARIADB_USER); + } + + @Test + @Order(19220) + @Commit + void persistDatabases() { + System.out.println("PERSISTING databases to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); + persistHostingAssetsOfType(PGSQL_DATABASE, MARIADB_DATABASE); + } + @Test @Order(19900) void verifyPersistedUnixUsersWithUserId() { @@ -483,7 +600,7 @@ public class ImportHostingAssets extends ImportOfficeData { @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); + assertThat(haCount).isGreaterThan(isImportingControlledTestData() ? 30 : 10000); } // ============================================================================================ @@ -517,11 +634,12 @@ public class ImportHostingAssets extends ImportOfficeData { // ============================================================================================ - private void persistHostingAssetsOfType(final HsHostingAssetType hsHostingAssetType) { + private void persistHostingAssetsOfType(final HsHostingAssetType... hsHostingAssetTypes) { + final var hsHostingAssetTypeSet = stream(hsHostingAssetTypes).collect(toSet()); jpaAttempt.transacted(() -> { hostingAssets.forEach((key, ha) -> { context(rbacSuperuser); - if (ha.getType() == hsHostingAssetType) { + if (hsHostingAssetTypeSet.contains(ha.getType())) { new HostingAssetEntitySaveProcessor(em, ha) .preprocessEntity() .validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*") @@ -750,7 +868,7 @@ public class ImportHostingAssets extends ImportOfficeData { .identifier(rec.getString("name")) .caption(rec.getString("comment")) .isLoaded(true) // avoid overwriting imported userids with generated ids - .config(new HashMap<>(Map.ofEntries( + .config(new HashMap<>(ofEntries( entry("shell", rec.getString("shell")), // entry("homedir", rec.getString("homedir")), do not import, it's calculated entry("locked", rec.getBoolean("locked")), @@ -806,7 +924,7 @@ public class ImportHostingAssets extends ImportOfficeData { .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .identifier(rec.getString("name")) .caption(rec.getString("name")) - .config(Map.ofEntries( + .config(ofEntries( entry("target", targets) )) .build(); @@ -814,6 +932,75 @@ public class ImportHostingAssets extends ImportOfficeData { }); } + private void importDatabaseUsers(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 dbuser_id = rec.getInteger("dbuser_id"); + final var packet_id = rec.getInteger("packet_id"); + final var engine = rec.getString("engine"); + final HsHostingAssetType dbUserAssetType = "mysql".equals(engine) ? MARIADB_USER + : "pgsql".equals(engine) ? PGSQL_USER + : failWith("unknown DB engine " + engine); + // FIXME: Create one instance for each managed server, not for each db-user! + final HsHostingAssetType dbInstanceAssetType = "mysql".equals(engine) ? MARIADB_INSTANCE + : "pgsql".equals(engine) ? PGSQL_INSTANCE + : failWith("unknown DB engine " + engine); + final var hash = dbUserAssetType == MARIADB_USER ? Algorithm.MYSQL_NATIVE : Algorithm.SCRAM_SHA256; + final var name = rec.getString("name"); + final var password_hash = rec.getString("password_hash", HashGenerator.using(hash).withSalt("fixed salt").hash("fake pw " + name)); + + final var dbInstanceAsset = HsHostingAssetRawEntity.builder() + .type(dbInstanceAssetType) + .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id).getParentAsset()) + .identifier(dbUserAssetType.name().substring(0, 2) + "I|" + name) + .caption(name) + .build(); + + final var dbUserAsset = HsHostingAssetRawEntity.builder() + .type(dbUserAssetType) + .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) + .assignedToAsset(dbInstanceAsset) + .identifier(dbUserAssetType.name().substring(0, 2) + "U|" + name) + .caption(name) + .config(new HashMap<>(ofEntries( + entry("password", password_hash) + ))) + .build(); + hostingAssets.put(DBUSER_ID_OFFSET + dbuser_id, dbUserAsset); + }); + } + + private void importDatabases(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 database_id = rec.getInteger("database_id"); + final var packet_id = rec.getInteger("packet_id"); + final var owning_dbuser_id = 0; // FIXME + final var engine = rec.getString("engine"); + final HsHostingAssetType type = "mysql".equals(engine) ? MARIADB_DATABASE + : "pgsql".equals(engine) ? PGSQL_DATABASE + : failWith("unknown DB engine " + engine); + final var name = rec.getString("name"); + final var encoding = rec.getString("encoding"); + final var dbUserAsset = HsHostingAssetRawEntity.builder() + .type(type) + .parentAsset(hostingAssets.get(DBUSER_ID_OFFSET + owning_dbuser_id)) + .identifier(type.name().substring(0, 2) + "D|" + name) + .caption(name) + .config(ofEntries( + entry("encoding", encoding) + )) + .build(); + hostingAssets.put(DB_ID_OFFSET + database_id, dbUserAsset); + }); + } + // ============================================================================================ V returning( diff --git a/src/test/resources/migration/hosting/database.csv b/src/test/resources/migration/hosting/database.csv new file mode 100644 index 00000000..d35bbda5 --- /dev/null +++ b/src/test/resources/migration/hosting/database.csv @@ -0,0 +1,23 @@ +database_id;engine;packet_id;name;owner;encoding + +77;pgsql;630;hsh00_vorstand;hsh00_vorstand;LATIN1 +786;mysql;630;hsh00_addr;hsh00;latin1 +805;mysql;630;hsh00_db2;hsh00;latin1 +291568;pgsql;1112;mih00_invoicing;mih00_invoicing;UTF8 + +1858;pgsql;630;hsh00;hsh00;LATIN1 +1860;pgsql;630;hsh00_hsadmin;hsh00_hsadmin;UTF8 + +4931;pgsql;630;hsh00_phpPgSqlAdmin;hsh00_phpPgSqlAdmin;UTF8 +4932;pgsql;630;hsh00_phpPgSqlAdmin_new;hsh00_phpPgSqlAdmin;UTF8 +4908;mysql;630;hsh00_mantis;hsh00_mantis;utf8 +4941;mysql;630;hsh00_phpMyAdmin;hsh00_phpMyAdmin;utf8 +4942;mysql;630;hsh00_phpMyAdmin_old;hsh00_phpMyAdmin;utf8 + +7520;mysql;1094;lug00_wla;lug00_wla;utf8 +7521;mysql;1094;lug00_wla_test;lug00_wla;utf8 +7522;pgsql;1094;lug00_ola;lug00_ola;UTF8 +7523;pgsql;1094;lug00_ola_Test;lug00_ola;UTF8 + +7604;mysql;1112;mim00_test;mim00_test;latin1 +7605;pgsql;1112;mim00_office;mim00_office;UTF8 diff --git a/src/test/resources/migration/hosting/database_user.csv b/src/test/resources/migration/hosting/database_user.csv new file mode 100644 index 00000000..5581740f --- /dev/null +++ b/src/test/resources/migration/hosting/database_user.csv @@ -0,0 +1,15 @@ +dbuser_id;engine;packet_id;name;password_hash + +1858;pgsql;630;hsh00 +1860;pgsql;630;hsh00_hsadmin +1861;pgsql;630;hsh00_hsadmin_ro +4931;mysql;630;hsh00_phpPgSqlAdmin +4908;mysql;630;hsh00_mantis +4909;mysql;630;hsh00_mantis_ro +4932;mysql;630;hsh00_phpMyAdmin + +7520;mysql;1094;lug00_wla +7522;pgsql;1094;lug00_ola + +7604;mysql;1112;mim00_test +7605;pgsql;1112;mim00_office -- 2.39.5 From 194405c577480a95159a6d551c28cb068ced8af7 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 2 Aug 2024 09:34:04 +0200 Subject: [PATCH 2/5] first working version including instance per engine and managed server and normalized encoding --- ...greSqlDbInstanceHostingAssetValidator.java | 4 +- .../hs/migration/ImportHostingAssets.java | 132 +++++++++++------- .../resources/migration/hosting/database.csv | 7 +- .../migration/hosting/database_user.csv | 24 ++-- 4 files changed, 103 insertions(+), 64 deletions(-) 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 36365597..70de55f9 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 @@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; import java.util.regex.Pattern; import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_DATABASE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_INSTANCE; class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityValidator { @@ -13,7 +13,7 @@ class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityVali public HsPostgreSqlDbInstanceHostingAssetValidator() { super( - PGSQL_DATABASE, + PGSQL_INSTANCE, AlarmContact.isOptional(), // TODO.spec: PostgreSQL extensions in database and here? also decide which. Free selection or booleans/checkboxes? 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 17943479..c974ba3e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -24,10 +24,12 @@ import org.springframework.test.annotation.Commit; import org.springframework.test.annotation.DirtiesContext; import java.io.Reader; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -107,8 +109,9 @@ public class ImportHostingAssets extends ImportOfficeData { static final Integer PACKET_ID_OFFSET = 3000000; static final Integer UNIXUSER_ID_OFFSET = 4000000; static final Integer EMAILALIAS_ID_OFFSET = 5000000; - static final Integer DBUSER_ID_OFFSET = 6000000; - static final Integer DB_ID_OFFSET = 7000000; + static final Integer DBINSTANCE_ID_OFFSET = 6000000; + static final Integer DBUSER_ID_OFFSET = 7000000; + static final Integer DB_ID_OFFSET = 8000000; record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference serverRef) {} @@ -116,6 +119,7 @@ public class ImportHostingAssets extends ImportOfficeData { static Map bookingItems = new WriteOnceMap<>(); static Map hives = new WriteOnceMap<>(); static Map hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type? + static Map dbUsersByEngineAndName = new WriteOnceMap<>(); @Test @Order(11010) @@ -348,18 +352,26 @@ public class ImportHostingAssets extends ImportOfficeData { @Test @Order(15000) void createDatabaseInstances() { - // FIXME + createDatabaseInstances(hostingAssets.values().stream().filter(ha -> ha.getType()==MANAGED_SERVER).toList()); } @Test @Order(15009) - void verifyDatabaseINSTANCES() { + void verifyDatabaseInstances() { assumeThatWeAreImportingControlledTestData(); -// FIXME -// assertThat(firstOfEachType(5, PGSQL_INSTANCE, MARIADB_INSTANCE)).isEqualToIgnoringWhitespace(""" -// { -// } -// """); + + assertThat(firstOfEachType(5, PGSQL_INSTANCE, MARIADB_INSTANCE)).isEqualToIgnoringWhitespace(""" + { + 6000000=HsHostingAssetRawEntity(PGSQL_INSTANCE, vm1061|PgSql.default, vm1061-PostgreSQL default instance, MANAGED_SERVER:vm1061), + 6000001=HsHostingAssetRawEntity(MARIADB_INSTANCE, vm1061|MariaDB.default, vm1061-MariaDB default instance, MANAGED_SERVER:vm1061), + 6000002=HsHostingAssetRawEntity(PGSQL_INSTANCE, vm1050|PgSql.default, vm1050-PostgreSQL default instance, MANAGED_SERVER:vm1050), + 6000003=HsHostingAssetRawEntity(MARIADB_INSTANCE, vm1050|MariaDB.default, vm1050-MariaDB default instance, MANAGED_SERVER:vm1050), + 6000004=HsHostingAssetRawEntity(PGSQL_INSTANCE, vm1068|PgSql.default, vm1068-PostgreSQL default instance, MANAGED_SERVER:vm1068), + 6000005=HsHostingAssetRawEntity(MARIADB_INSTANCE, vm1068|MariaDB.default, vm1068-MariaDB default instance, MANAGED_SERVER:vm1068), + 6000006=HsHostingAssetRawEntity(PGSQL_INSTANCE, vm1093|PgSql.default, vm1093-PostgreSQL default instance, MANAGED_SERVER:vm1093), + 6000007=HsHostingAssetRawEntity(MARIADB_INSTANCE, vm1093|MariaDB.default, vm1093-MariaDB default instance, MANAGED_SERVER:vm1093) + } + """); } @Test @@ -380,16 +392,16 @@ public class ImportHostingAssets extends ImportOfficeData { assertThat(firstOfEachType(5, PGSQL_USER, MARIADB_USER)).isEqualToIgnoringWhitespace(""" { - 6001858=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00, hsh00, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:PGI|hsh00, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$JDiZmaxU+O+ByArLY/CkYZ8HbOk0r/I8LyABnno5gQs=:NI3T500/63dzI1B07Jh3UtQGlukS6JxuS0XoxM/QgAc="}), - 6001860=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_hsadmin, hsh00_hsadmin, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:PGI|hsh00_hsadmin, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$54Wh+OGx/GaIvAia+I3k78jHGhqmYwe4+iLssmH5zhk=:D4Gq1z2Li2BVSaZrz1azDrs6pwsIzhq4+suK1Hh6ZIg="}), - 6001861=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_hsadmin_ro, hsh00_hsadmin_ro, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:PGI|hsh00_hsadmin_ro, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$UhJnJJhmKANbcaG+izWK3rz5bmhhluSuiCJFlUmDVI8=:6AC4mbLfJGiGlEOWhpz9BivvMODhLLHOnRnnktJPgn8="}), - 6004908=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_mantis, hsh00_mantis, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_mantis, { "password": "*EA4C0889A22AAE66BBEBC88161E8CF862D73B44F"}), - 6004909=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_mantis_ro, hsh00_mantis_ro, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_mantis_ro, { "password": "*B3BB6D0DA2EC01958616E9B3BCD2926FE8C38383"}), - 6004931=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_phpPgSqlAdmin, hsh00_phpPgSqlAdmin, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_phpPgSqlAdmin, { "password": "*59067A36BA197AD0A47D74909296C5B002A0FB9F"}), - 6004932=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_phpMyAdmin, hsh00_phpMyAdmin, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:MAI|hsh00_phpMyAdmin, { "password": "*3188720B1889EF5447C722629765F296F40257C2"}), - 6007520=HsHostingAssetRawEntity(MARIADB_USER, MAU|lug00_wla, lug00_wla, MANAGED_WEBSPACE:lug00, MARIADB_INSTANCE:MAI|lug00_wla, { "password": "*11667C0EAC42BF8B0295ABEDC7D2868A835E4DB5"}), - 6007522=HsHostingAssetRawEntity(PGSQL_USER, PGU|lug00_ola, lug00_ola, MANAGED_WEBSPACE:lug00, PGSQL_INSTANCE:PGI|lug00_ola, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$tir+cV3ZzOZeEWurwAJk+8qkvsTAWaBfwx846oYMOr4=:p4yk/4hHkfSMAFxSuTuh3RIrbSpHNBh7h6raVa3nt1c="}), - 6007605=HsHostingAssetRawEntity(PGSQL_USER, PGU|mim00_office, mim00_office, MANAGED_WEBSPACE:mim00, PGSQL_INSTANCE:PGI|mim00_office, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$43jziwd1o+nkfjE0zFbks24Zy5GK+km87B7vzEQt4So=:xRQntZxBxdo1JJbhkegnUFKHT0T8MDW75hkQs2S3z6k="}) + 7001857=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00, hsh00, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:vm1050|PgSql.default, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$JDiZmaxU+O+ByArLY/CkYZ8HbOk0r/I8LyABnno5gQs=:NI3T500/63dzI1B07Jh3UtQGlukS6JxuS0XoxM/QgAc="}), + 7001858=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00, hsh00, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:vm1050|MariaDB.default, { "password": "*59067A36BA197AD0A47D74909296C5B002A0FB9F"}), + 7001859=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_vorstand, hsh00_vorstand, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:vm1050|PgSql.default, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$54Wh+OGx/GaIvAia+I3k78jHGhqmYwe4+iLssmH5zhk=:D4Gq1z2Li2BVSaZrz1azDrs6pwsIzhq4+suK1Hh6ZIg="}), + 7001860=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_hsadmin, hsh00_hsadmin, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:vm1050|PgSql.default, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$54Wh+OGx/GaIvAia+I3k78jHGhqmYwe4+iLssmH5zhk=:D4Gq1z2Li2BVSaZrz1azDrs6pwsIzhq4+suK1Hh6ZIg="}), + 7001861=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_hsadmin_ro, hsh00_hsadmin_ro, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:vm1050|PgSql.default, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$UhJnJJhmKANbcaG+izWK3rz5bmhhluSuiCJFlUmDVI8=:6AC4mbLfJGiGlEOWhpz9BivvMODhLLHOnRnnktJPgn8="}), + 7004908=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_mantis, hsh00_mantis, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:vm1050|MariaDB.default, { "password": "*EA4C0889A22AAE66BBEBC88161E8CF862D73B44F"}), + 7004909=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_mantis_ro, hsh00_mantis_ro, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:vm1050|MariaDB.default, { "password": "*B3BB6D0DA2EC01958616E9B3BCD2926FE8C38383"}), + 7004931=HsHostingAssetRawEntity(PGSQL_USER, PGU|hsh00_phpPgSqlAdmin, hsh00_phpPgSqlAdmin, MANAGED_WEBSPACE:hsh00, PGSQL_INSTANCE:vm1050|PgSql.default, { "password": "SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$UhJnJJhmKANbcaG+izWK3rz5bmhhluSuiCJFlUmDVI8=:6AC4mbLfJGiGlEOWhpz9BivvMODhLLHOnRnnktJPgn8="}), + 7004932=HsHostingAssetRawEntity(MARIADB_USER, MAU|hsh00_phpMyAdmin, hsh00_phpMyAdmin, MANAGED_WEBSPACE:hsh00, MARIADB_INSTANCE:vm1050|MariaDB.default, { "password": "*3188720B1889EF5447C722629765F296F40257C2"}), + 7007520=HsHostingAssetRawEntity(MARIADB_USER, MAU|lug00_wla, lug00_wla, MANAGED_WEBSPACE:lug00, MARIADB_INSTANCE:vm1068|MariaDB.default, { "password": "*11667C0EAC42BF8B0295ABEDC7D2868A835E4DB5"}) } """); } @@ -412,16 +424,16 @@ public class ImportHostingAssets extends ImportOfficeData { assertThat(firstOfEachType(5, PGSQL_DATABASE, MARIADB_DATABASE)).isEqualToIgnoringWhitespace(""" { - 7000077=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_vorstand, hsh00_vorstand, { "encoding": "LATIN1"}), - 7000786=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_addr, hsh00_addr, { "encoding": "latin1"}), - 7000805=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_db2, hsh00_db2, { "encoding": "latin1"}), - 7001858=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00, hsh00, { "encoding": "LATIN1"}), - 7001860=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_hsadmin, hsh00_hsadmin, { "encoding": "UTF8"}), - 7004908=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_mantis, hsh00_mantis, { "encoding": "utf8"}), - 7004931=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_phpPgSqlAdmin, hsh00_phpPgSqlAdmin, { "encoding": "UTF8"}), - 7004932=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_phpPgSqlAdmin_new, hsh00_phpPgSqlAdmin_new, { "encoding": "UTF8"}), - 7004941=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_phpMyAdmin, hsh00_phpMyAdmin, { "encoding": "utf8"}), - 7004942=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_phpMyAdmin_old, hsh00_phpMyAdmin_old, { "encoding": "utf8"}) + 8000077=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_vorstand, hsh00_vorstand, PGSQL_USER:PGU|hsh00_vorstand, { "encoding": "LATIN1"}), + 8000786=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_addr, hsh00_addr, MARIADB_USER:MAU|hsh00, { "encoding": "latin1"}), + 8000805=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_db2, hsh00_db2, MARIADB_USER:MAU|hsh00, { "encoding": "latin1"}), + 8001858=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00, hsh00, PGSQL_USER:PGU|hsh00, { "encoding": "LATIN1"}), + 8001860=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_hsadmin, hsh00_hsadmin, PGSQL_USER:PGU|hsh00_hsadmin, { "encoding": "UTF8"}), + 8004908=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_mantis, hsh00_mantis, MARIADB_USER:MAU|hsh00_mantis, { "encoding": "utf8"}), + 8004931=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_phpPgSqlAdmin, hsh00_phpPgSqlAdmin, PGSQL_USER:PGU|hsh00_phpPgSqlAdmin, { "encoding": "UTF8"}), + 8004932=HsHostingAssetRawEntity(PGSQL_DATABASE, PGD|hsh00_phpPgSqlAdmin_new, hsh00_phpPgSqlAdmin_new, PGSQL_USER:PGU|hsh00_phpPgSqlAdmin, { "encoding": "UTF8"}), + 8004941=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_phpMyAdmin, hsh00_phpMyAdmin, MARIADB_USER:MAU|hsh00_phpMyAdmin, { "encoding": "utf8"}), + 8004942=HsHostingAssetRawEntity(MARIADB_DATABASE, MAD|hsh00_phpMyAdmin_old, hsh00_phpMyAdmin_old, MARIADB_USER:MAU|hsh00_phpMyAdmin, { "encoding": "utf8"}) } """); } @@ -932,6 +944,33 @@ public class ImportHostingAssets extends ImportOfficeData { }); } + private void createDatabaseInstances(final List parentAssets) { + final var idRef = new AtomicInteger(0); + parentAssets.forEach(pa -> { + if (pa.getSubHostingAssets() == null) { + pa.setSubHostingAssets(new ArrayList<>()); + } + + final var pgSqlInstanceAsset = HsHostingAssetRawEntity.builder() + .type(PGSQL_INSTANCE) + .parentAsset(pa) + .identifier(pa.getIdentifier() + "|PgSql.default") + .caption(pa.getIdentifier() + "-PostgreSQL default instance") + .build(); + pa.getSubHostingAssets().add(pgSqlInstanceAsset); + hostingAssets.put(DBINSTANCE_ID_OFFSET + idRef.getAndIncrement(), pgSqlInstanceAsset); + + final var mariaDbInstanceAsset = HsHostingAssetRawEntity.builder() + .type(MARIADB_INSTANCE) + .parentAsset(pa) + .identifier(pa.getIdentifier() + "|MariaDB.default") + .caption(pa.getIdentifier() + "-MariaDB default instance") + .build(); + pa.getSubHostingAssets().add(mariaDbInstanceAsset); + hostingAssets.put(DBINSTANCE_ID_OFFSET + idRef.getAndIncrement(), mariaDbInstanceAsset); + }); + } + private void importDatabaseUsers(final String[] header, final List records) { final var columns = new Columns(header); records.stream() @@ -944,20 +983,17 @@ public class ImportHostingAssets extends ImportOfficeData { final HsHostingAssetType dbUserAssetType = "mysql".equals(engine) ? MARIADB_USER : "pgsql".equals(engine) ? PGSQL_USER : failWith("unknown DB engine " + engine); - // FIXME: Create one instance for each managed server, not for each db-user! + final var hash = dbUserAssetType == MARIADB_USER ? Algorithm.MYSQL_NATIVE : Algorithm.SCRAM_SHA256; + final var name = rec.getString("name"); + final var password_hash = rec.getString("password_hash", HashGenerator.using(hash).withRandomSalt().hash("fake pw " + name)); + final HsHostingAssetType dbInstanceAssetType = "mysql".equals(engine) ? MARIADB_INSTANCE : "pgsql".equals(engine) ? PGSQL_INSTANCE : failWith("unknown DB engine " + engine); - final var hash = dbUserAssetType == MARIADB_USER ? Algorithm.MYSQL_NATIVE : Algorithm.SCRAM_SHA256; - final var name = rec.getString("name"); - final var password_hash = rec.getString("password_hash", HashGenerator.using(hash).withSalt("fixed salt").hash("fake pw " + name)); - - final var dbInstanceAsset = HsHostingAssetRawEntity.builder() - .type(dbInstanceAssetType) - .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id).getParentAsset()) - .identifier(dbUserAssetType.name().substring(0, 2) + "I|" + name) - .caption(name) - .build(); + final var relatedWebspaceHA = hostingAssets.get(PACKET_ID_OFFSET + packet_id).getParentAsset(); + final var dbInstanceAsset = relatedWebspaceHA.getSubHostingAssets().stream() + .filter(ha -> ha.getType() == dbInstanceAssetType) + .findAny().orElseThrow(); // there is exactly one: the default instance for the given type final var dbUserAsset = HsHostingAssetRawEntity.builder() .type(dbUserAssetType) @@ -969,6 +1005,7 @@ public class ImportHostingAssets extends ImportOfficeData { entry("password", password_hash) ))) .build(); + dbUsersByEngineAndName.put(engine + ":" + name, dbUserAsset); hostingAssets.put(DBUSER_ID_OFFSET + dbuser_id, dbUserAsset); }); } @@ -980,24 +1017,25 @@ public class ImportHostingAssets extends ImportOfficeData { .map(row -> new Record(columns, row)) .forEach(rec -> { final var database_id = rec.getInteger("database_id"); - final var packet_id = rec.getInteger("packet_id"); - final var owning_dbuser_id = 0; // FIXME final var engine = rec.getString("engine"); + final var owner = rec.getString("owner"); + final var owningDbUserHA = dbUsersByEngineAndName.get(engine + ":" + owner); + assertThat(owningDbUserHA).as("owning user for " + (engine + ":" + owner) + " not found").isNotNull(); final HsHostingAssetType type = "mysql".equals(engine) ? MARIADB_DATABASE : "pgsql".equals(engine) ? PGSQL_DATABASE : failWith("unknown DB engine " + engine); final var name = rec.getString("name"); - final var encoding = rec.getString("encoding"); - final var dbUserAsset = HsHostingAssetRawEntity.builder() + final var encoding = rec.getString("encoding").replaceAll("[-_]+", ""); + final var dbAsset = HsHostingAssetRawEntity.builder() .type(type) - .parentAsset(hostingAssets.get(DBUSER_ID_OFFSET + owning_dbuser_id)) + .parentAsset(owningDbUserHA) .identifier(type.name().substring(0, 2) + "D|" + name) .caption(name) .config(ofEntries( - entry("encoding", encoding) + entry("encoding", type == MARIADB_DATABASE ? encoding.toLowerCase() : encoding.toUpperCase()) )) .build(); - hostingAssets.put(DB_ID_OFFSET + database_id, dbUserAsset); + hostingAssets.put(DB_ID_OFFSET + database_id, dbAsset); }); } diff --git a/src/test/resources/migration/hosting/database.csv b/src/test/resources/migration/hosting/database.csv index d35bbda5..e992d086 100644 --- a/src/test/resources/migration/hosting/database.csv +++ b/src/test/resources/migration/hosting/database.csv @@ -2,15 +2,14 @@ database_id;engine;packet_id;name;owner;encoding 77;pgsql;630;hsh00_vorstand;hsh00_vorstand;LATIN1 786;mysql;630;hsh00_addr;hsh00;latin1 -805;mysql;630;hsh00_db2;hsh00;latin1 -291568;pgsql;1112;mih00_invoicing;mih00_invoicing;UTF8 +805;mysql;630;hsh00_db2;hsh00;LATIN-1 1858;pgsql;630;hsh00;hsh00;LATIN1 1860;pgsql;630;hsh00_hsadmin;hsh00_hsadmin;UTF8 4931;pgsql;630;hsh00_phpPgSqlAdmin;hsh00_phpPgSqlAdmin;UTF8 -4932;pgsql;630;hsh00_phpPgSqlAdmin_new;hsh00_phpPgSqlAdmin;UTF8 -4908;mysql;630;hsh00_mantis;hsh00_mantis;utf8 +4932;pgsql;630;hsh00_phpPgSqlAdmin_new;hsh00_phpPgSqlAdmin;utf8 +4908;mysql;630;hsh00_mantis;hsh00_mantis;UTF-8 4941;mysql;630;hsh00_phpMyAdmin;hsh00_phpMyAdmin;utf8 4942;mysql;630;hsh00_phpMyAdmin_old;hsh00_phpMyAdmin;utf8 diff --git a/src/test/resources/migration/hosting/database_user.csv b/src/test/resources/migration/hosting/database_user.csv index 5581740f..33018673 100644 --- a/src/test/resources/migration/hosting/database_user.csv +++ b/src/test/resources/migration/hosting/database_user.csv @@ -1,15 +1,17 @@ dbuser_id;engine;packet_id;name;password_hash -1858;pgsql;630;hsh00 -1860;pgsql;630;hsh00_hsadmin -1861;pgsql;630;hsh00_hsadmin_ro -4931;mysql;630;hsh00_phpPgSqlAdmin -4908;mysql;630;hsh00_mantis -4909;mysql;630;hsh00_mantis_ro -4932;mysql;630;hsh00_phpMyAdmin +1857;pgsql;630;hsh00;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$JDiZmaxU+O+ByArLY/CkYZ8HbOk0r/I8LyABnno5gQs=:NI3T500/63dzI1B07Jh3UtQGlukS6JxuS0XoxM/QgAc= +1858;mysql;630;hsh00;*59067A36BA197AD0A47D74909296C5B002A0FB9F +1859;pgsql;630;hsh00_vorstand;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$54Wh+OGx/GaIvAia+I3k78jHGhqmYwe4+iLssmH5zhk=:D4Gq1z2Li2BVSaZrz1azDrs6pwsIzhq4+suK1Hh6ZIg= +1860;pgsql;630;hsh00_hsadmin;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$54Wh+OGx/GaIvAia+I3k78jHGhqmYwe4+iLssmH5zhk=:D4Gq1z2Li2BVSaZrz1azDrs6pwsIzhq4+suK1Hh6ZIg= +1861;pgsql;630;hsh00_hsadmin_ro;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$UhJnJJhmKANbcaG+izWK3rz5bmhhluSuiCJFlUmDVI8=:6AC4mbLfJGiGlEOWhpz9BivvMODhLLHOnRnnktJPgn8= +4931;pgsql;630;hsh00_phpPgSqlAdmin;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$UhJnJJhmKANbcaG+izWK3rz5bmhhluSuiCJFlUmDVI8=:6AC4mbLfJGiGlEOWhpz9BivvMODhLLHOnRnnktJPgn8= +4908;mysql;630;hsh00_mantis;*EA4C0889A22AAE66BBEBC88161E8CF862D73B44F +4909;mysql;630;hsh00_mantis_ro;*B3BB6D0DA2EC01958616E9B3BCD2926FE8C38383 +4932;mysql;630;hsh00_phpMyAdmin;*3188720B1889EF5447C722629765F296F40257C2 -7520;mysql;1094;lug00_wla -7522;pgsql;1094;lug00_ola +7520;mysql;1094;lug00_wla;*11667C0EAC42BF8B0295ABEDC7D2868A835E4DB5 +7522;pgsql;1094;lug00_ola;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$tir+cV3ZzOZeEWurwAJk+8qkvsTAWaBfwx846oYMOr4=:p4yk/4hHkfSMAFxSuTuh3RIrbSpHNBh7h6raVa3nt1c= -7604;mysql;1112;mim00_test -7605;pgsql;1112;mim00_office +7604;mysql;1112;mim00_test;*156CFD94A0594A5C3F4C6742376DDF4B8C5F6D90 +7605;pgsql;1112;mim00_office;SCRAM-SHA-256$4096:Zml4ZWQgc2FsdA==$43jziwd1o+nkfjE0zFbks24Zy5GK+km87B7vzEQt4So=:xRQntZxBxdo1JJbhkegnUFKHT0T8MDW75hkQs2S3z6k= -- 2.39.5 From a0f1fcbfb84ee19b9aa6d58cf2dedfc1ec8d0a3c Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 2 Aug 2024 09:53:05 +0200 Subject: [PATCH 3/5] amendments after self-review --- .../hostsharing/hsadminng/hash/HashGenerator.java | 7 ++++++- .../HsPostgreSqlUserHostingAssetValidator.java | 8 -------- .../hsadminng/hs/validation/HsEntityValidator.java | 14 +------------- .../hsadminng/hs/validation/PasswordProperty.java | 2 ++ .../hs/migration/ImportHostingAssets.java | 1 + 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java b/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java index a577f3ce..44f41281 100644 --- a/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java @@ -27,6 +27,7 @@ public final class HashGenerator { "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789/."; + private static boolean couldBeHashEnabled; // TODO.impl: remove after legacy data is migrated public enum Algorithm { LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"), @@ -59,8 +60,12 @@ public final class HashGenerator { this.algorithm = algorithm; } + public static void enableChouldBeHash(final boolean enable) { + couldBeHashEnabled = enable; + } + public boolean couldBeHash(final String value) { - return value.startsWith(algorithm.prefix); + return couldBeHashEnabled && value.startsWith(algorithm.prefix); } public String hash(final String plaintextPassword) { 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 736648ff..e10b6e6c 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 @@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; -import java.util.List; import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_USER; @@ -28,13 +27,6 @@ class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.SCRAM_SHA256).writeOnly()); } - // FIXME: remove method - @Override - public List validateEntity(final HsHostingAsset assetEntity) { - final var result = super.validateEntity(assetEntity); - return result; - } - @Override protected Pattern identifierPattern(final HsHostingAsset assetEntity) { final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); 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 0ba440d2..77cc2514 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -5,7 +5,6 @@ package net.hostsharing.hsadminng.hs.validation; import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -63,18 +62,7 @@ public abstract class HsEntityValidator { } protected ArrayList validateProperties(final PropertiesProvider propsProvider) { - final var result = new ArrayList() { - - @Override - public boolean add(final String s) { - return super.add(s); - } - - @Override - public boolean addAll(final Collection c) { - return super.addAll(c); - } - }; + final var result = new ArrayList(); // verify that all actually given properties are specified final var properties = propsProvider.directProps(); 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 fa23b1d2..ceaf2603 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java @@ -31,10 +31,12 @@ public class PasswordProperty extends StringProperty { @Override protected void validate(final List result, final String propValue, final PropertiesProvider propProvider) { + // TODO.impl: remove after legacy data is migrated if (HashGenerator.using(hashedUsing).couldBeHash(propValue) && propValue.length() > this.maxLength()) { // already hashed => do not validate return; } + super.validate(result, propValue, propProvider); validatePassword(result, propValue); } 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 c974ba3e..288261e7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -972,6 +972,7 @@ public class ImportHostingAssets extends ImportOfficeData { } private void importDatabaseUsers(final String[] header, final List records) { + HashGenerator.enableChouldBeHash(true); final var columns = new Columns(header); records.stream() .map(this::trimAll) -- 2.39.5 From b8b7a848979bd4d99a2779c3737e0b6c84cb0a37 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 2 Aug 2024 10:06:17 +0200 Subject: [PATCH 4/5] CREATE VIEW hs_statistics_view --- src/main/resources/db/changelog/9-hs-global/9000-statistics.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql b/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql index f4c841e9..7c4304b3 100644 --- a/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql +++ b/src/main/resources/db/changelog/9-hs-global/9000-statistics.sql @@ -3,6 +3,7 @@ -- ============================================================================ --changeset hs-global-object-statistics:1 endDelimiter:--// -- ---------------------------------------------------------------------------- +CREATE VIEW hs_statistics_view AS select * from (select count, "table" as "rbac-table", '' as "hs-table", '' as "type" from rbacstatisticsview -- 2.39.5 From c9c8e9b5b25d066cc9daa9779f072d22d85712d2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 2 Aug 2024 10:39:10 +0200 Subject: [PATCH 5/5] cleanup after code-review --- .../hostsharing/hsadminng/hs/migration/CsvDataImport.java | 8 -------- 1 file changed, 8 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 70337bcb..2b68e352 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/CsvDataImport.java @@ -343,14 +343,6 @@ class Columns { int indexOf(final String columnName) { return columnNames.indexOf(columnName); } - - int indexOfOrFail(final String columnName) { - int index = columnNames.indexOf(columnName); - if (index < 0) { - throw new RuntimeException("column name '" + columnName + "' not found in: " + columnNames); - } - return index; - } } class Record { -- 2.39.5