From b37cb10e8f76c6de6b3f48eebc157d5d8d923015 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jul 2024 12:43:31 +0200 Subject: [PATCH 1/6] add HsMariaDbInstanceHostingAssetValidator --- .../HostingAssetEntityValidatorRegistry.java | 1 + ...sMariaDbInstanceHostingAssetValidator.java | 37 ++++++ .../hs-hosting/hs-hosting-asset-schemas.yaml | 1 + .../7010-hs-hosting-asset.sql | 2 + .../7018-hs-hosting-asset-test-data.sql | 28 +++-- .../HsHostingAssetControllerRestTest.java | 20 +++ ...ingAssetPropsControllerAcceptanceTest.java | 3 +- ...HostingAssetRepositoryIntegrationTest.java | 3 +- ...gAssetEntityValidatorRegistryUnitTest.java | 3 +- ...InstanceHostingAssetValidatorUnitTest.java | 116 ++++++++++++++++++ 10 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidatorUnitTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java index c44bf92a..aa4657b4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java @@ -26,6 +26,7 @@ public class HostingAssetEntityValidatorRegistry { register(DOMAIN_SMTP_SETUP, new HsDomainSmtpSetupHostingAssetValidator()); register(DOMAIN_MBOX_SETUP, new HsDomainMboxSetupHostingAssetValidator()); register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator()); + register(MARIADB_INSTANCE, new HsMariaDbInstanceHostingAssetValidator()); } private static void register(final Enum type, final HsEntityValidator validator) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java new file mode 100644 index 00000000..88bfb50d --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidator.java @@ -0,0 +1,37 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; + +import java.util.regex.Pattern; + +import static java.util.Optional.ofNullable; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE; + +class HsMariaDbInstanceHostingAssetValidator extends HostingAssetEntityValidator { + + final static String DEFAULT_INSTANCE_IDENTIFIER_SUFFIX = "|MariaDB.default"; // TODO.spec: specify instance naming + + public HsMariaDbInstanceHostingAssetValidator() { + super( + MARIADB_INSTANCE, + AlarmContact.isOptional(), // hostmaster alert address is implicitly added + NO_EXTRA_PROPERTIES); // TODO.spec: specify instance properties, e.g. installed extensions + } + + @Override + protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + return Pattern.compile( + "^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX) + + "$"); + } + + @Override + public void preprocessEntity(final HsHostingAssetEntity entity) { + super.preprocessEntity(entity); + if (entity.getIdentifier() == null) { + ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier( + pa.getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX)); + } + } +} diff --git a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml index f4a25607..d467031f 100644 --- a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml +++ b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml @@ -19,6 +19,7 @@ components: - EMAIL_ADDRESS - PGSQL_USER - PGSQL_DATABASE + - MARIADB_INSTANCE - MARIADB_USER - MARIADB_DATABASE diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index c1b4bbcc..70f102c2 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -18,6 +18,7 @@ create type HsHostingAssetType as enum ( 'EMAIL_ADDRESS', 'PGSQL_USER', 'PGSQL_DATABASE', + 'MARIADB_INSTANCE', 'MARIADB_USER', 'MARIADB_DATABASE' ); @@ -74,6 +75,7 @@ begin when 'EMAIL_ADDRESS' then 'DOMAIN_MBOX_SETUP' when 'PGSQL_USER' then 'MANAGED_WEBSPACE' when 'PGSQL_DATABASE' then 'MANAGED_WEBSPACE' + when 'MARIADB_INSTANCE' then 'MANAGED_SERVER' when 'MARIADB_USER' then 'MANAGED_WEBSPACE' when 'MARIADB_DATABASE' then 'MANAGED_WEBSPACE' else raiseException(format('[400] unknown asset type %s', NEW.type::text)) diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql index f43edef0..0d4b6a91 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql @@ -25,6 +25,7 @@ declare webUnixUserUuid uuid; domainSetupUuid uuid; domainMBoxSetupUuid uuid; + mariaDbInstanceUuid uuid; begin currentTask := 'creating hosting-asset test-data ' || givenProjectCaption; call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); @@ -69,22 +70,25 @@ begin select uuid_generate_v4() into webUnixUserUuid; select uuid_generate_v4() into domainSetupUuid; select uuid_generate_v4() into domainMBoxSetupUuid; + select uuid_generate_v4() into mariaDbInstanceUuid; debitorNumberSuffix := relatedDebitor.debitorNumberSuffix; defaultPrefix := relatedDebitor.defaultPrefix; insert into hs_hosting_asset - (uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config) - values (managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb), - (uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), - (managedWebspaceUuid, managedWebspaceBI.uuid, 'MANAGED_WEBSPACE', managedServerUuid, null, defaultPrefix || '01', 'some Webspace', '{}'::jsonb), - (uuid_generate_v4(), null, 'EMAIL_ALIAS', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some E-Mail-Alias', '{ "target": [ "office@example.org", "archive@example.com" ] }'::jsonb), - (webUnixUserUuid, null, 'UNIX_USER', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some UnixUser for Website', '{ "SSD-soft-quota": "128", "SSD-hard-quota": "256", "HDD-soft-quota": "512", "HDD-hard-quota": "1024"}'::jsonb), - (domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_DNS_SETUP', domainSetupUuid, null, defaultPrefix || '.example.org|DNS', 'some Domain-DNS-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_HTTP_SETUP', domainSetupUuid, webUnixUserUuid, defaultPrefix || '.example.org|HTTP', 'some Domain-HTTP-Setup', '{ "option-htdocsfallback": true, "use-fcgiphpbin": "/usr/lib/cgi-bin/php", "validsubdomainnames": "*"}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_SMTP_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-SMPT-Setup', '{}'::jsonb), - (domainMBoxSetupUuid, null, 'DOMAIN_MBOX_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-MBOX-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'EMAIL_ADDRESS', domainMBoxSetupUuid, null, 'test@' || defaultPrefix || '.example.org', 'some E-Mail-Address', '{}'::jsonb); + (uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config) + values + (managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb), + (uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), + (mariaDbInstanceUuid, null, 'MARIADB_INSTANCE', managedServerUuid, null, 'vm10' || debitorNumberSuffix || '.MariaDB.default','some default MariaDB instance','{}'::jsonb), + (managedWebspaceUuid, managedWebspaceBI.uuid, 'MANAGED_WEBSPACE', managedServerUuid, null, defaultPrefix || '01', 'some Webspace', '{}'::jsonb), + (uuid_generate_v4(), null, 'EMAIL_ALIAS', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some E-Mail-Alias', '{ "target": [ "office@example.org", "archive@example.com" ] }'::jsonb), + (webUnixUserUuid, null, 'UNIX_USER', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some UnixUser for Website', '{ "SSD-soft-quota": "128", "SSD-hard-quota": "256", "HDD-soft-quota": "512", "HDD-hard-quota": "1024"}'::jsonb), + (domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb), + (uuid_generate_v4(), null, 'DOMAIN_DNS_SETUP', domainSetupUuid, null, defaultPrefix || '.example.org|DNS', 'some Domain-DNS-Setup', '{}'::jsonb), + (uuid_generate_v4(), null, 'DOMAIN_HTTP_SETUP', domainSetupUuid, webUnixUserUuid, defaultPrefix || '.example.org|HTTP', 'some Domain-HTTP-Setup', '{ "option-htdocsfallback": true, "use-fcgiphpbin": "/usr/lib/cgi-bin/php", "validsubdomainnames": "*"}'::jsonb), + (uuid_generate_v4(), null, 'DOMAIN_SMTP_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-SMPT-Setup', '{}'::jsonb), + (domainMBoxSetupUuid, null, 'DOMAIN_MBOX_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-MBOX-Setup', '{}'::jsonb), + (uuid_generate_v4(), null, 'EMAIL_ADDRESS', domainMBoxSetupUuid, null, 'test@' || defaultPrefix || '.example.org', 'some E-Mail-Address', '{}'::jsonb); end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 4a5bc04c..0dc425f8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -30,6 +30,7 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM; import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_SERVER_BOOKING_ITEM; +import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET; import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET; import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; @@ -363,6 +364,25 @@ public class HsHostingAssetControllerRestTest { } } ] + """), + MARIADB_INSTANCE( + List.of( + HsHostingAssetEntity.builder() + .type(HsHostingAssetType.MARIADB_INSTANCE) + .parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) + .identifier("vm1234|MariaDB.default") + .caption("some fake MariaDB instance") + .build()), + """ + [ + { + "type": "MARIADB_INSTANCE", + "identifier": "vm1234|MariaDB.default", + "caption": "some fake MariaDB instance", + "alarmContact": null, + "config": {} + } + ] """); final HsHostingAssetType assetType; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java index 290777ea..9020d076 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java @@ -41,7 +41,8 @@ class HsHostingAssetPropsControllerAcceptanceTest { "DOMAIN_HTTP_SETUP", "DOMAIN_SMTP_SETUP", "DOMAIN_MBOX_SETUP", - "EMAIL_ADDRESS" + "EMAIL_ADDRESS", + "MARIADB_INSTANCE" ] """)); // @formatter:on diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java index 40f38d7b..8d62e4cb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java @@ -245,7 +245,8 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu // then exactlyTheseAssetsAreReturned( result, - "HsHostingAssetEntity(MANAGED_WEBSPACE, sec01, some Webspace, MANAGED_SERVER:vm1012, D-1000212:D-1000212 default project:separate ManagedWebspace)"); + "HsHostingAssetEntity(MANAGED_WEBSPACE, sec01, some Webspace, MANAGED_SERVER:vm1012, D-1000212:D-1000212 default project:separate ManagedWebspace)", + "HsHostingAssetEntity(MARIADB_INSTANCE, vm1012.MariaDB.default, some default MariaDB instance, MANAGED_SERVER:vm1012)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java index c1c8a53c..887a0368 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java @@ -39,7 +39,8 @@ class HostingAssetEntityValidatorRegistryUnitTest { HsHostingAssetType.DOMAIN_HTTP_SETUP, HsHostingAssetType.DOMAIN_SMTP_SETUP, HsHostingAssetType.DOMAIN_MBOX_SETUP, - HsHostingAssetType.EMAIL_ADDRESS + HsHostingAssetType.EMAIL_ADDRESS, + HsHostingAssetType.MARIADB_INSTANCE ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidatorUnitTest.java new file mode 100644 index 00000000..24d8b4d1 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbInstanceHostingAssetValidatorUnitTest.java @@ -0,0 +1,116 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static java.util.Map.entry; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE; +import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET; +import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsMariaDbInstanceHostingAssetValidator.DEFAULT_INSTANCE_IDENTIFIER_SUFFIX; +import static org.assertj.core.api.Assertions.assertThat; + +class HsMariaDbInstanceHostingAssetValidatorUnitTest { + + static HsHostingAssetEntityBuilder validEntityBuilder() { + return HsHostingAssetEntity.builder() + .type(MARIADB_INSTANCE) + .parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) + .identifier(TEST_MANAGED_SERVER_HOSTING_ASSET.getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX); + } + + @Test + void containsExpectedProperties() { + // when + final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SMTP_SETUP); + + // then + assertThat(validator.properties()).map(Map::toString).isEmpty(); + } + + @Test + void preprocessesTakesIdentifierFromParent() { + // given + final var givenEntity = validEntityBuilder().build(); + assertThat(givenEntity.getParentAsset().getIdentifier()).as("precondition failed").isEqualTo("vm1234"); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + validator.preprocessEntity(givenEntity); + + // then + assertThat(givenEntity.getIdentifier()).isEqualTo("vm1234|MariaDB.default"); + } + + @Test + void acceptsValidEntity() { + // given + final var givenEntity = validEntityBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).isEmpty(); + } + + @Test + void rejectsInvalidIdentifier() { + // given + final var givenEntity = validEntityBuilder().identifier("example.org").build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).containsExactly( + "'identifier' expected to match '^\\Qvm1234|MariaDB.default\\E$', but is 'example.org'" + ); + } + + @Test + void rejectsInvalidReferencedEntities() { + // given + final var mangedServerHostingAssetEntity = validEntityBuilder() + .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) + .parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(mangedServerHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'MARIADB_INSTANCE:vm1234|MariaDB.default.bookingItem' must be null but is of type CLOUD_SERVER", + "'MARIADB_INSTANCE:vm1234|MariaDB.default.parentAsset' must be of type MANAGED_SERVER but is of type MANAGED_WEBSPACE", + "'MARIADB_INSTANCE:vm1234|MariaDB.default.assignedToAsset' must be null but is of type MANAGED_WEBSPACE"); + } + + @Test + void rejectsInvalidProperties() { + // given + final var mangedServerHostingAssetEntity = validEntityBuilder() + .config(Map.ofEntries( + entry("any", "false") + )) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(mangedServerHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'MARIADB_INSTANCE:vm1234|MariaDB.default.config.any' is not expected but is set to 'false'"); + } +} -- 2.39.5 From 7c82d53323b996a062e7b54cd043402264ce77c3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jul 2024 14:40:49 +0200 Subject: [PATCH 2/6] add HsMariaDbUserHostingAssetValidator --- .../HostingAssetEntityValidatorRegistry.java | 1 + .../HsMariaDbUserHostingAssetValidator.java | 33 +++++ .../7010-hs-hosting-asset.sql | 4 +- .../7018-hs-hosting-asset-test-data.sql | 25 ++-- .../HsHostingAssetControllerRestTest.java | 18 +++ ...ingAssetPropsControllerAcceptanceTest.java | 3 +- ...gAssetEntityValidatorRegistryUnitTest.java | 3 +- ...iaDbUserHostingAssetValidatorUnitTest.java | 123 ++++++++++++++++++ 8 files changed, 194 insertions(+), 16 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidatorUnitTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java index aa4657b4..bf93e19f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java @@ -27,6 +27,7 @@ public class HostingAssetEntityValidatorRegistry { register(DOMAIN_MBOX_SETUP, new HsDomainMboxSetupHostingAssetValidator()); register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator()); register(MARIADB_INSTANCE, new HsMariaDbInstanceHostingAssetValidator()); + register(MARIADB_USER, new HsMariaDbUserHostingAssetValidator()); } private static void register(final Enum type, final HsEntityValidator validator) { 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 new file mode 100644 index 00000000..778bd7ba --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java @@ -0,0 +1,33 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; + +import java.util.regex.Pattern; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER; +import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty; + +class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator { + + public HsMariaDbUserHostingAssetValidator() { + super( + MARIADB_USER, + AlarmContact.isOptional(), + + // TODO.impl: we need to be able to suppress updating of fields etc., something like this: + // withFieldValidation( + // referenceProperty(alarmContact).isOptional(), + // referenceProperty(parentAsset).isWriteOnce(), + // referenceProperty(assignedToAsset).isWriteOnce(), + // ); + + passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LinuxEtcShadowHashGenerator.Algorithm.SHA512).writeOnly()); + } + + @Override + protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + final var webspaceIdentifier = assetEntity.getAssignedToAsset().getIdentifier(); + return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9]+$"); + } +} diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index 70f102c2..ece84d9c 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -76,8 +76,8 @@ begin when 'PGSQL_USER' then 'MANAGED_WEBSPACE' when 'PGSQL_DATABASE' then 'MANAGED_WEBSPACE' when 'MARIADB_INSTANCE' then 'MANAGED_SERVER' - when 'MARIADB_USER' then 'MANAGED_WEBSPACE' - when 'MARIADB_DATABASE' then 'MANAGED_WEBSPACE' + when 'MARIADB_USER' then 'MARIADB_INSTANCE' + when 'MARIADB_DATABASE' then 'MARIADB_INSTANCE' else raiseException(format('[400] unknown asset type %s', NEW.type::text)) end); diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql index 0d4b6a91..efc76846 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql @@ -77,18 +77,19 @@ begin insert into hs_hosting_asset (uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config) values - (managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb), - (uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), - (mariaDbInstanceUuid, null, 'MARIADB_INSTANCE', managedServerUuid, null, 'vm10' || debitorNumberSuffix || '.MariaDB.default','some default MariaDB instance','{}'::jsonb), - (managedWebspaceUuid, managedWebspaceBI.uuid, 'MANAGED_WEBSPACE', managedServerUuid, null, defaultPrefix || '01', 'some Webspace', '{}'::jsonb), - (uuid_generate_v4(), null, 'EMAIL_ALIAS', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some E-Mail-Alias', '{ "target": [ "office@example.org", "archive@example.com" ] }'::jsonb), - (webUnixUserUuid, null, 'UNIX_USER', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some UnixUser for Website', '{ "SSD-soft-quota": "128", "SSD-hard-quota": "256", "HDD-soft-quota": "512", "HDD-hard-quota": "1024"}'::jsonb), - (domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_DNS_SETUP', domainSetupUuid, null, defaultPrefix || '.example.org|DNS', 'some Domain-DNS-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_HTTP_SETUP', domainSetupUuid, webUnixUserUuid, defaultPrefix || '.example.org|HTTP', 'some Domain-HTTP-Setup', '{ "option-htdocsfallback": true, "use-fcgiphpbin": "/usr/lib/cgi-bin/php", "validsubdomainnames": "*"}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_SMTP_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-SMPT-Setup', '{}'::jsonb), - (domainMBoxSetupUuid, null, 'DOMAIN_MBOX_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-MBOX-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'EMAIL_ADDRESS', domainMBoxSetupUuid, null, 'test@' || defaultPrefix || '.example.org', 'some E-Mail-Address', '{}'::jsonb); + (managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb), + (uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), + (mariaDbInstanceUuid, null, 'MARIADB_INSTANCE', managedServerUuid, null, 'vm10' || debitorNumberSuffix || '.MariaDB.default', 'some default MariaDB instance','{}'::jsonb), + (uuid_generate_v4(), null, 'MARIADB_USER', mariaDbInstanceUuid, null, defaultPrefix || '01_web', 'some default MariaDB user', '{ "password": "(ofEntries( + entry("password", "Hallo Datenbank, lass mich rein!") + ))); + } + + @Test + void describesItsProperties() { + // given + final var validator = HostingAssetEntityValidatorRegistry.forType(givenValidMariaDbUserBuilder().build().getType()); + + // when + final var props = validator.properties(); + + // then + assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( + "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}" + ); + } + + @Test + void preparesEntity() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + LinuxEtcShadowHashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); // FIXME + validator.prepareProperties(givenMariaDbUserHostingAsset); + + // then + assertThat(givenMariaDbUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries( + entry("password", "$6$Ly3LbsArtL5u4EVt$T7VM5uCq7I1cKttipCX4TQdyawdpLcSmjApreI4fZcORPOkEkxy9iz.9Dri6IVbO08OKTR8OE8hvnblU5Ax6o.") + )); + } + + @Test + void validatesValidEntity() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + final var result = Stream.concat( + validator.validateEntity(givenMariaDbUserHostingAsset).stream(), + validator.validateContext(givenMariaDbUserHostingAsset).stream() + ).toList(); + + // then + assertThat(result).isEmpty(); + } + + @Test + void rejectsInvalidProperties() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder() + .config(ofEntries( + entry("unknown", 100), + entry("password", "short") + )) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + final var result = validator.validateEntity(givenMariaDbUserHostingAsset); + + // 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" + ); + } + + @Test + void rejectsInvalidIdentifier() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder() + .identifier("xyz99-temp") + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + final var result = validator.validateEntity(givenMariaDbUserHostingAsset); + + // then + assertThat(result).containsExactly( + "'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9]+$', but is 'xyz99-temp'"); + } +} -- 2.39.5 From 95b830322aa8967ceb31e96a10fa30a6055a9746 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jul 2024 16:31:40 +0200 Subject: [PATCH 3/6] add HsMariaDbDatabaseHostingAssetValidator --- .../hs/hosting/asset/HsHostingAssetType.java | 4 +- .../HostingAssetEntityValidatorRegistry.java | 1 + ...sMariaDbDatabaseHostingAssetValidator.java | 26 ++++ .../7018-hs-hosting-asset-test-data.sql | 7 +- .../HsHostingAssetControllerRestTest.java | 25 ++++ ...ingAssetPropsControllerAcceptanceTest.java | 3 +- .../asset/HsHostingAssetTypeUnitTest.java | 4 +- ...gAssetEntityValidatorRegistryUnitTest.java | 3 +- ...DatabaseHostingAssetValidatorUnitTest.java | 121 ++++++++++++++++++ 9 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidatorUnitTest.java 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 0054974b..669c4ca1 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 @@ -105,8 +105,8 @@ public enum HsHostingAssetType implements Node { MARIADB_DATABASE( // named e.g. xyz00_abc inGroup("MariaDB"), - requiredParent(MANAGED_WEBSPACE), // TODO.spec: or MARIADB_USER? - assignedTo(MARIADB_INSTANCE)), // TODO.spec: or swapping parent+assignedTo? + requiredParent(MARIADB_INSTANCE), // TODO.spec: or MARIADB_USER? + assignedTo(MARIADB_USER)), // TODO.spec: or swapping parent+assignedTo? IP_NUMBER( inGroup("Server"), diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java index bf93e19f..be6778cb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java @@ -28,6 +28,7 @@ public class HostingAssetEntityValidatorRegistry { register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator()); register(MARIADB_INSTANCE, new HsMariaDbInstanceHostingAssetValidator()); register(MARIADB_USER, new HsMariaDbUserHostingAssetValidator()); + register(MARIADB_DATABASE, new HsMariaDbDatabaseHostingAssetValidator()); } private static void register(final Enum type, final HsEntityValidator validator) { 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 new file mode 100644 index 00000000..593556e6 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidator.java @@ -0,0 +1,26 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; + +import java.util.regex.Pattern; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_DATABASE; +import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty; + +class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator { + + public HsMariaDbDatabaseHostingAssetValidator() { + super( + MARIADB_DATABASE, + AlarmContact.isOptional(), + + stringProperty("encoding").matchesRegEx("[a-z0-9_]+").maxLength(24).provided("latin2", "utf8").withDefault("utf8"), + stringProperty("collation").matchesRegEx("[a-z0-9_]+").maxLength(24).provided("latin2", "utf8").withDefault("utf8")); + } + + @Override + protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + final var webspaceIdentifier = assetEntity.getAssignedToAsset().getAssignedToAsset().getIdentifier(); + return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9]+$"); + } +} diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql index efc76846..c7fc4450 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql @@ -26,6 +26,7 @@ declare domainSetupUuid uuid; domainMBoxSetupUuid uuid; mariaDbInstanceUuid uuid; + mariaDbUserUuid uuid; begin currentTask := 'creating hosting-asset test-data ' || givenProjectCaption; call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); @@ -71,6 +72,7 @@ begin select uuid_generate_v4() into domainSetupUuid; select uuid_generate_v4() into domainMBoxSetupUuid; select uuid_generate_v4() into mariaDbInstanceUuid; + select uuid_generate_v4() into mariaDbUserUuid; debitorNumberSuffix := relatedDebitor.debitorNumberSuffix; defaultPrefix := relatedDebitor.defaultPrefix; @@ -79,9 +81,10 @@ begin values (managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb), (uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), - (mariaDbInstanceUuid, null, 'MARIADB_INSTANCE', managedServerUuid, null, 'vm10' || debitorNumberSuffix || '.MariaDB.default', 'some default MariaDB instance','{}'::jsonb), - (uuid_generate_v4(), null, 'MARIADB_USER', mariaDbInstanceUuid, null, defaultPrefix || '01_web', 'some default MariaDB user', '{ "password": " HA_MANAGED_SERVER HA_MARIADB_USER *==> HA_MARIADB_INSTANCE HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE - HA_MARIADB_DATABASE *==> HA_MANAGED_WEBSPACE - HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE + HA_MARIADB_DATABASE *==> HA_MARIADB_INSTANCE + HA_MARIADB_DATABASE o..> HA_MARIADB_USER HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java index 00f07198..61bf0ea8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java @@ -41,7 +41,8 @@ class HostingAssetEntityValidatorRegistryUnitTest { HsHostingAssetType.DOMAIN_MBOX_SETUP, HsHostingAssetType.EMAIL_ADDRESS, HsHostingAssetType.MARIADB_INSTANCE, - HsHostingAssetType.MARIADB_USER + HsHostingAssetType.MARIADB_USER, + HsHostingAssetType.MARIADB_DATABASE ); } } 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 new file mode 100644 index 00000000..c75a135f --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbDatabaseHostingAssetValidatorUnitTest.java @@ -0,0 +1,121 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.stream.Stream; + +import static java.util.Map.ofEntries; +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.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET; +import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET; +import static net.hostsharing.hsadminng.mapper.PatchMap.entry; +import static org.assertj.core.api.Assertions.assertThat; + +class HsMariaDbDatabaseHostingAssetValidatorUnitTest { + + private static final HsHostingAssetEntity GIVEN_MARIADB_INSTANCE = HsHostingAssetEntity.builder() + .type(MARIADB_INSTANCE) + .parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) + .identifier("vm1234|MariaDB.default") + .caption("some valid test MariaDB-Instance") + .build(); + + private static final HsHostingAssetEntity GIVEN_MARIADB_USER = HsHostingAssetEntity.builder() + .type(MARIADB_USER) + .parentAsset(GIVEN_MARIADB_INSTANCE) + .assignedToAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) + .identifier("xyz00_temp") + .caption("some valid test MariaDB-User") + .config(new HashMap<>(ofEntries( + entry("password", "Hallo Datenbank, lass mich rein!") + ))) + .build(); + + private static HsHostingAssetEntityBuilder givenValidMariaDbDatabaseBuilder() { + return HsHostingAssetEntity.builder() + .type(MARIADB_DATABASE) + .parentAsset(GIVEN_MARIADB_INSTANCE) + .assignedToAsset(GIVEN_MARIADB_USER) + .identifier("xyz00_temp") + .caption("some valid test MariaDB-Database") + .config(new HashMap<>(ofEntries( + entry("encoding", "latin1"), + entry("collation", "latin1") + ))); + } + + @Test + void describesItsProperties() { + // given + final var validator = HostingAssetEntityValidatorRegistry.forType(givenValidMariaDbDatabaseBuilder().build().getType()); + + // when + final var props = validator.properties(); + + // then + assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( + "{type=string, propertyName=encoding, matchesRegEx=[[a-z0-9_]+], maxLength=24, provided=[latin2, utf8], defaultValue=utf8}", + "{type=string, propertyName=collation, matchesRegEx=[[a-z0-9_]+], maxLength=24, provided=[latin2, utf8], defaultValue=utf8}" + ); + } + + @Test + void validatesValidEntity() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + final var result = Stream.concat( + validator.validateEntity(givenMariaDbUserHostingAsset).stream(), + validator.validateContext(givenMariaDbUserHostingAsset).stream() + ).toList(); + + // then + assertThat(result).isEmpty(); + } + + @Test + void rejectsInvalidProperties() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder() + .config(ofEntries( + entry("unknown", "wrong"), + entry("encoding", 10), + entry("collation", 20) + )) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + final var result = validator.validateEntity(givenMariaDbUserHostingAsset); + + // 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:xyz00_temp.config.collation' is expected to be of type String, but is of type Integer" + ); + } + + @Test + void rejectsInvalidIdentifier() { + // given + final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder() + .identifier("xyz99-temp") + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); + + // when + final var result = validator.validateEntity(givenMariaDbUserHostingAsset); + + // then + assertThat(result).containsExactly( + "'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9]+$', but is 'xyz99-temp'"); + } +} -- 2.39.5 From 045361ade510fb7ee712a9e6a76d5a8e255939b6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 12 Jul 2024 10:18:52 +0200 Subject: [PATCH 4/6] password hash --- .../hsadminng/hash/HashGenerator.java | 89 +++++++++++++++ .../hash/LinuxEtcShadowHashGenerator.java | 108 +++--------------- .../MySQLNativePasswordHashGenerator.java | 35 ++++++ .../HsMariaDbUserHostingAssetValidator.java | 4 +- .../HsUnixUserHostingAssetValidator.java | 5 +- .../hs/validation/PasswordProperty.java | 6 +- .../hsadminng/hash/HashGeneratorUnitTest.java | 56 +++++++++ .../LinuxEtcShadowHashGeneratorUnitTest.java | 51 --------- ...sHostingAssetControllerAcceptanceTest.java | 4 +- ...iaDbUserHostingAssetValidatorUnitTest.java | 9 +- ...UnixUserHostingAssetValidatorUnitTest.java | 6 +- .../validation/PasswordPropertyUnitTest.java | 8 +- 12 files changed, 217 insertions(+), 164 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hash/MySQLNativePasswordHashGenerator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hash/HashGeneratorUnitTest.java delete mode 100644 src/test/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGeneratorUnitTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java b/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java new file mode 100644 index 00000000..345f0ed0 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hash/HashGenerator.java @@ -0,0 +1,89 @@ +package net.hostsharing.hsadminng.hash; + +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.function.BiFunction; +import java.util.random.RandomGenerator; + +import lombok.Getter; + +/** + * Usage-example to generate hash: + * HashGenerator.using(LINUX_SHA512).withRandomSalt().hash("plaintext password"); + * + * Usage-example to verify hash: + * HashGenerator.fromHash("hashed password).verify("plaintext password"); + */ +@Getter +public final class HashGenerator { + + private static final RandomGenerator random = new SecureRandom(); + private static final Queue predefinedSalts = new PriorityQueue<>(); + + public static final int RANDOM_SALT_LENGTH = 16; + private static final String RANDOM_SALT_CHARACTERS = + "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "0123456789/."; + + public enum Algorithm { + LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"), + LINUX_YESCRYPT(LinuxEtcShadowHashGenerator::hash, "y"), + MYSQL_NATIVE(MySQLNativePasswordHashGenerator::hash, "*"); + + final BiFunction implementation; + final String prefix; + + Algorithm(BiFunction implementation, final String prefix) { + this.implementation = implementation; + this.prefix = prefix; + } + + static Algorithm byPrefix(final String prefix) { + return Arrays.stream(Algorithm.values()).filter(a -> a.prefix.equals(prefix)).findAny() + .orElseThrow(() -> new IllegalArgumentException("unknown hash algorithm: '" + prefix + "'")); + } + } + + private final Algorithm algorithm; + private String salt; + + public static HashGenerator using(final Algorithm algorithm) { + return new HashGenerator(algorithm); + } + + private HashGenerator(final Algorithm algorithm) { + this.algorithm = algorithm; + } + + public String hash(final String plaintextPassword) { + if (plaintextPassword == null) { + throw new IllegalStateException("no password given"); + } + + return algorithm.implementation.apply(this, plaintextPassword); + } + + public static void nextSalt(final String salt) { + predefinedSalts.add(salt); + } + + public HashGenerator withSalt(final String salt) { + this.salt = salt; + return this; + } + + public HashGenerator withRandomSalt() { + if (!predefinedSalts.isEmpty()) { + return withSalt(predefinedSalts.poll()); + } + final var stringBuilder = new StringBuilder(RANDOM_SALT_LENGTH); + for (int i = 0; i < RANDOM_SALT_LENGTH; ++i) { + int randomIndex = random.nextInt(RANDOM_SALT_CHARACTERS.length()); + stringBuilder.append(RANDOM_SALT_CHARACTERS.charAt(randomIndex)); + } + return withSalt(stringBuilder.toString()); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGenerator.java b/src/main/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGenerator.java index c030b830..aaed6fd0 100644 --- a/src/main/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGenerator.java @@ -1,107 +1,31 @@ package net.hostsharing.hsadminng.hash; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.random.RandomGenerator; - import com.sun.jna.Library; import com.sun.jna.Native; public class LinuxEtcShadowHashGenerator { - private static final RandomGenerator random = new SecureRandom(); - private static final Queue predefinedSalts = new PriorityQueue<>(); - - public static final int SALT_LENGTH = 16; - - private final String plaintextPassword; - private Algorithm algorithm; - - public enum Algorithm { - SHA512("6"), - YESCRYPT("y"); - - final String prefix; - - Algorithm(final String prefix) { - this.prefix = prefix; - } - - static Algorithm byPrefix(final String prefix) { - return Arrays.stream(Algorithm.values()).filter(a -> a.prefix.equals(prefix)).findAny() - .orElseThrow(() -> new IllegalArgumentException("unknown hash algorithm: '" + prefix + "'")); - } - } - - private static final String SALT_CHARACTERS = - "abcdefghijklmnopqrstuvwxyz" + - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "0123456789/."; - - private String salt; - - public static LinuxEtcShadowHashGenerator hash(final String plaintextPassword) { - return new LinuxEtcShadowHashGenerator(plaintextPassword); - } - - private LinuxEtcShadowHashGenerator(final String plaintextPassword) { - this.plaintextPassword = plaintextPassword; - } - - public LinuxEtcShadowHashGenerator using(final Algorithm algorithm) { - this.algorithm = algorithm; - return this; - } - - void verify(final String givenHash) { - final var parts = givenHash.split("\\$"); - if (parts.length < 3 || parts.length > 5) { - throw new IllegalArgumentException("not a " + algorithm.name() + " Linux hash: " + givenHash); - } - - algorithm = Algorithm.byPrefix(parts[1]); - salt = parts.length == 4 ? parts[2] : parts[2] + "$" + parts[3]; - - if (!generate().equals(givenHash)) { - throw new IllegalArgumentException("invalid password"); - } - } - - public String generate() { - if (salt == null) { + public static String hash(final HashGenerator generator, final String payload) { + if (generator.getSalt() == null) { throw new IllegalStateException("no salt given"); } - if (plaintextPassword == null) { - throw new IllegalStateException("no password given"); + + return NativeCryptLibrary.INSTANCE.crypt(payload, "$" + generator.getAlgorithm().prefix + "$" + generator.getSalt()); + } + + public static void verify(final String givenHash, final String payload) { + + final var parts = givenHash.split("\\$"); + if (parts.length < 3 || parts.length > 5) { + throw new IllegalArgumentException("hash with unknown hash method: " + givenHash); } - return NativeCryptLibrary.INSTANCE.crypt(plaintextPassword, "$" + algorithm.prefix + "$" + salt); - } - - public static void nextSalt(final String salt) { - predefinedSalts.add(salt); - } - - public LinuxEtcShadowHashGenerator withSalt(final String salt) { - this.salt = salt; - return this; - } - - public LinuxEtcShadowHashGenerator withRandomSalt() { - if (!predefinedSalts.isEmpty()) { - return withSalt(predefinedSalts.poll()); + final var algorithm = HashGenerator.Algorithm.byPrefix(parts[1]); + final var salt = parts.length == 4 ? parts[2] : parts[2] + "$" + parts[3]; + final var calcualatedHash = HashGenerator.using(algorithm).withSalt(salt).hash(payload); + if (!calcualatedHash.equals(givenHash)) { + throw new IllegalArgumentException("invalid password"); } - final var stringBuilder = new StringBuilder(SALT_LENGTH); - for (int i = 0; i < SALT_LENGTH; ++i) { - int randomIndex = random.nextInt(SALT_CHARACTERS.length()); - stringBuilder.append(SALT_CHARACTERS.charAt(randomIndex)); - } - return withSalt(stringBuilder.toString()); - } - public static void main(String[] args) { - System.out.println(NativeCryptLibrary.INSTANCE.crypt("given password", "$6$abcdefghijklmno")); } public interface NativeCryptLibrary extends Library { diff --git a/src/main/java/net/hostsharing/hsadminng/hash/MySQLNativePasswordHashGenerator.java b/src/main/java/net/hostsharing/hsadminng/hash/MySQLNativePasswordHashGenerator.java new file mode 100644 index 00000000..12eeed44 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hash/MySQLNativePasswordHashGenerator.java @@ -0,0 +1,35 @@ +package net.hostsharing.hsadminng.hash; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class MySQLNativePasswordHashGenerator { + + public static String hash(final HashGenerator generator, final String password) { + // TODO.impl: if a random salt is generated or not should be part of the algorithm definition +// if (generator.getSalt() != null) { +// throw new IllegalStateException("salt not supported"); +// } + + try { + final var sha1 = MessageDigest.getInstance("SHA-1"); + final var firstHash = sha1.digest(password.getBytes()); + final var secondHash = sha1.digest(firstHash); + return "*" + bytesToHex(secondHash).toUpperCase(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("SHA-1 algorithm not found", e); + } + } + + private static String bytesToHex(byte[] bytes) { + final var hexString = new StringBuilder(); + for (byte b : bytes) { + final var hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } +} 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 778bd7ba..0b99e56c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; +import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import java.util.regex.Pattern; @@ -22,7 +22,7 @@ class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator { // referenceProperty(assignedToAsset).isWriteOnce(), // ); - passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LinuxEtcShadowHashGenerator.Algorithm.SHA512).writeOnly()); + passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.MYSQL_NATIVE).writeOnly()); } @Override diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java index 1d44f6ac..6a4299f1 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidator.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; +import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; @@ -30,7 +30,8 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { .withDefault("/bin/false"), stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir), stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(), - passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LinuxEtcShadowHashGenerator.Algorithm.SHA512).writeOnly()); + passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly()); + // TODO.spec: private SSH keys? } @Override 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 83cdf975..732151cd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/PasswordProperty.java @@ -1,13 +1,13 @@ package net.hostsharing.hsadminng.hs.validation; -import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.Algorithm; +import net.hostsharing.hsadminng.hash.HashGenerator; +import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm; import lombok.Setter; import java.util.List; import java.util.stream.Stream; import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.hash; import static net.hostsharing.hsadminng.mapper.Array.insertNewEntriesAfterExistingEntry; @Setter @@ -36,7 +36,7 @@ public class PasswordProperty extends StringProperty { this.hashedUsing = algorithm; computedBy((entity) -> ofNullable(entity.getDirectValue(propertyName, String.class)) - .map(password -> hash(password).using(algorithm).withRandomSalt().generate()) + .map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password)) .orElse(null)); return self(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hash/HashGeneratorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hash/HashGeneratorUnitTest.java new file mode 100644 index 00000000..6c70bc8e --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hash/HashGeneratorUnitTest.java @@ -0,0 +1,56 @@ +package net.hostsharing.hsadminng.hash; + +import org.junit.jupiter.api.Test; + +import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.LINUX_SHA512; +import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.MYSQL_NATIVE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +class HashGeneratorUnitTest { + + final String GIVEN_PASSWORD = "given password"; + final String WRONG_PASSWORD = "wrong password"; + final String GIVEN_SALT = "0123456789abcdef"; + + // generated via mkpasswd for plaintext password GIVEN_PASSWORD (see above) + final String GIVEN_LINUX_SHA512_HASH = "$6$ooei1HK6JXVaI7KC$sY5d9fEOr36hjh4CYwIKLMfRKL1539bEmbVCZ.zPiH0sv7jJVnoIXb5YEefEtoSM2WWgDi9hr7vXRe3Nw8zJP/"; + final String GIVEN_LINUX_YESCRYPT_HASH = "$y$j9T$wgYACPmBXvlMg2MzeZA0p1$KXUzd28nG.67GhPnBZ3aZsNNA5bWFdL/dyG4wS0iRw7"; + + @Test + void verifiesLinuxPasswordAgainstSha512HashFromMkpasswd() { + LinuxEtcShadowHashGenerator.verify(GIVEN_LINUX_SHA512_HASH, GIVEN_PASSWORD); // throws exception if wrong + } + + @Test + void verifiesLinuxPasswordAgainstYescryptHashFromMkpasswd() { + LinuxEtcShadowHashGenerator.verify(GIVEN_LINUX_YESCRYPT_HASH, GIVEN_PASSWORD); // throws exception if wrong + } + + @Test + void verifiesHashedLinuxPasswordWithRandomSalt() { + final var hash = HashGenerator.using(LINUX_SHA512).withRandomSalt().hash(GIVEN_PASSWORD); + LinuxEtcShadowHashGenerator.verify(hash, GIVEN_PASSWORD); // throws exception if wrong + } + + @Test + void verifiesLinuxHashedPasswordWithGivenSalt() { + final var hash = HashGenerator.using(LINUX_SHA512).withSalt(GIVEN_SALT).hash(GIVEN_PASSWORD); + LinuxEtcShadowHashGenerator.verify(hash, GIVEN_PASSWORD); // throws exception if wrong + } + + @Test + void throwsExceptionForInvalidLinuxPassword() { + final var hash = HashGenerator.using(LINUX_SHA512).withRandomSalt().hash(GIVEN_PASSWORD); + final var throwable = catchThrowable(() -> + LinuxEtcShadowHashGenerator.verify(hash, WRONG_PASSWORD) + ); + assertThat(throwable).hasMessage("invalid password"); + } + + @Test + void verifiesMySqlNativePassword() { + final var hash = HashGenerator.using(MYSQL_NATIVE).hash("Test1234"); + assertThat(hash).isEqualTo("*14F1A8C42F8B6D4662BB3ED290FD37BF135FE45C"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGeneratorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGeneratorUnitTest.java deleted file mode 100644 index c5abcc08..00000000 --- a/src/test/java/net/hostsharing/hsadminng/hash/LinuxEtcShadowHashGeneratorUnitTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.hostsharing.hsadminng.hash; - -import org.junit.jupiter.api.Test; - -import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.Algorithm.SHA512; -import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.hash; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; - -class LinuxEtcShadowHashGeneratorUnitTest { - - final String GIVEN_PASSWORD = "given password"; - final String WRONG_PASSWORD = "wrong password"; - final String GIVEN_SALT = "0123456789abcdef"; - - // generated via mkpasswd for plaintext password GIVEN_PASSWORD (see above) - final String GIVEN_SHA512_HASH = "$6$ooei1HK6JXVaI7KC$sY5d9fEOr36hjh4CYwIKLMfRKL1539bEmbVCZ.zPiH0sv7jJVnoIXb5YEefEtoSM2WWgDi9hr7vXRe3Nw8zJP/"; - final String GIVEN_YESCRYPT_HASH = "$y$j9T$wgYACPmBXvlMg2MzeZA0p1$KXUzd28nG.67GhPnBZ3aZsNNA5bWFdL/dyG4wS0iRw7"; - - @Test - void verifiesPasswordAgainstSha512HashFromMkpasswd() { - hash(GIVEN_PASSWORD).verify(GIVEN_SHA512_HASH); // throws exception if wrong - } - - @Test - void verifiesPasswordAgainstYescryptHashFromMkpasswd() { - hash(GIVEN_PASSWORD).verify(GIVEN_YESCRYPT_HASH); // throws exception if wrong - } - - @Test - void verifiesHashedPasswordWithRandomSalt() { - final var hash = hash(GIVEN_PASSWORD).using(SHA512).withRandomSalt().generate(); - hash(GIVEN_PASSWORD).verify(hash); // throws exception if wrong - } - - @Test - void verifiesHashedPasswordWithGivenSalt() { - final var givenPasswordHash =hash(GIVEN_PASSWORD).using(SHA512).withSalt(GIVEN_SALT).generate(); - hash(GIVEN_PASSWORD).verify(givenPasswordHash); // throws exception if wrong - } - - @Test - void throwsExceptionForInvalidPassword() { - final var givenPasswordHash = hash(GIVEN_PASSWORD).using(SHA512).withRandomSalt().generate(); - - final var throwable = catchThrowable(() -> - hash(WRONG_PASSWORD).verify(givenPasswordHash) // throws exception if wrong); - ); - assertThat(throwable).hasMessage("invalid password"); - } -} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java index e45a157b..28933662 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset; import io.restassured.RestAssured; import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; -import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; +import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRepository; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; @@ -537,7 +537,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup .identifier("fir01-temp") .caption("some test-unix-user") .build()); - LinuxEtcShadowHashGenerator.nextSalt("Jr5w/Y8zo8pCkqg7"); + HashGenerator.nextSalt("Jr5w/Y8zo8pCkqg7"); RestAssured // @formatter:off .given() 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 18683a68..7406a6fe 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 @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; import org.junit.jupiter.api.Test; @@ -33,7 +32,7 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { .identifier("xyz00_temp") .caption("some valid test MariaDB-User") .config(new HashMap<>(ofEntries( - entry("password", "Hallo Datenbank, lass mich rein!") + entry("password", "Test1234") ))); } @@ -47,7 +46,7 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { // then assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( - "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}" + "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=MYSQL_NATIVE, undisclosed=true}" ); } @@ -58,12 +57,12 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); // when - LinuxEtcShadowHashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); // FIXME + // HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); // not needed for mysql_native_password validator.prepareProperties(givenMariaDbUserHostingAsset); // then assertThat(givenMariaDbUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries( - entry("password", "$6$Ly3LbsArtL5u4EVt$T7VM5uCq7I1cKttipCX4TQdyawdpLcSmjApreI4fZcORPOkEkxy9iz.9Dri6IVbO08OKTR8OE8hvnblU5Ax6o.") + entry("password", "*14F1A8C42F8B6D4662BB3ED290FD37BF135FE45C") )); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java index 9a17eb27..61cebf95 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; +import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import org.junit.jupiter.api.Test; @@ -50,7 +50,7 @@ class HsUnixUserHostingAssetValidatorUnitTest { final var validator = HostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType()); // when - LinuxEtcShadowHashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); + HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); validator.prepareProperties(unixUserHostingAsset); // then @@ -141,7 +141,7 @@ class HsUnixUserHostingAssetValidatorUnitTest { final var validator = HostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType()); // when - LinuxEtcShadowHashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); + HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); final var result = validator.revampProperties(unixUserHostingAsset, unixUserHostingAsset.getConfig()); // then diff --git a/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java index 2350b288..663a7715 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/validation/PasswordPropertyUnitTest.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.validation; +import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -8,8 +9,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.Algorithm.SHA512; -import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.hash; +import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.LINUX_SHA512; import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty; import static net.hostsharing.hsadminng.mapper.PatchableMapWrapper.entry; import static org.assertj.core.api.Assertions.assertThat; @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; class PasswordPropertyUnitTest { private final ValidatableProperty passwordProp = - passwordProperty("password").minLength(8).maxLength(40).hashedUsing(SHA512).writeOnly(); + passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LINUX_SHA512).writeOnly(); private final List violations = new ArrayList<>(); @ParameterizedTest @@ -115,6 +115,6 @@ class PasswordPropertyUnitTest { }); // then - hash("some password").using(SHA512).withRandomSalt().generate(); // throws exception if wrong + LinuxEtcShadowHashGenerator.verify(result, "some password"); // throws exception if wrong } } -- 2.39.5 From 8dac2c9d7ebdd6cc530ef77e6b6224d220d3540b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 12 Jul 2024 10:44:28 +0200 Subject: [PATCH 5/6] database *==> user *==> webspace --- doc/hs-hosting-asset-type-structure.md | 81 +++++++++---------- .../hs/hosting/asset/HsHostingAssetType.java | 8 +- ...sMariaDbDatabaseHostingAssetValidator.java | 5 +- .../HsMariaDbUserHostingAssetValidator.java | 2 +- .../asset/HsHostingAssetTypeUnitTest.java | 14 ++-- ...DatabaseHostingAssetValidatorUnitTest.java | 20 ++--- ...iaDbUserHostingAssetValidatorUnitTest.java | 4 +- ...UnixUserHostingAssetValidatorUnitTest.java | 2 +- 8 files changed, 65 insertions(+), 71 deletions(-) diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md index f2af876b..f7310316 100644 --- a/doc/hs-hosting-asset-type-structure.md +++ b/doc/hs-hosting-asset-type-structure.md @@ -1,5 +1,6 @@ ## HostingAsset Type Structure + ### Domain ```plantuml @@ -11,29 +12,27 @@ package Booking #feb28c { entity BI_CLOUD_SERVER entity BI_MANAGED_SERVER entity BI_MANAGED_WEBSPACE - entity BI_DOMAIN_DNS_SETUP - entity BI_DOMAIN_SMTP_SETUP } package Hosting #feb28c{ package Domain #99bcdb { - entity HA_DOMAIN_SETUP - entity HA_DOMAIN_DNS_SETUP - entity HA_DOMAIN_HTTP_SETUP - entity HA_DOMAIN_SMTP_SETUP - entity HA_DOMAIN_MBOX_SETUP + entity HA_DOMAIN_SETUP + entity HA_DOMAIN_DNS_SETUP + entity HA_DOMAIN_HTTP_SETUP + entity HA_DOMAIN_SMTP_SETUP + entity HA_DOMAIN_MBOX_SETUP entity HA_EMAIL_ADDRESS } package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER entity HA_IP_NUMBER } package Webspace #99bcdb { - entity HA_MANAGED_WEBSPACE - entity HA_UNIX_USER + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER entity HA_EMAIL_ALIAS } @@ -43,20 +42,21 @@ BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER -HA_CLOUD_SERVER ==* BI_CLOUD_SERVER -HA_MANAGED_SERVER ==* BI_MANAGED_SERVER -HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE +HA_CLOUD_SERVER *==> BI_CLOUD_SERVER +HA_MANAGED_SERVER *==> BI_MANAGED_SERVER +HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER HA_UNIX_USER *==> HA_MANAGED_WEBSPACE +HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP +HA_DOMAIN_DNS_SETUP o..> HA_MANAGED_WEBSPACE HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER HA_DOMAIN_SMTP_SETUP *==> HA_DOMAIN_SETUP HA_DOMAIN_SMTP_SETUP o..> HA_MANAGED_WEBSPACE HA_DOMAIN_MBOX_SETUP *==> HA_DOMAIN_SETUP HA_DOMAIN_MBOX_SETUP o..> HA_MANAGED_WEBSPACE -HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_SETUP HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER @@ -70,6 +70,7 @@ package Legend #white { } Booking -down[hidden]->Legend ``` + ### MariaDB ```plantuml @@ -81,26 +82,24 @@ package Booking #feb28c { entity BI_CLOUD_SERVER entity BI_MANAGED_SERVER entity BI_MANAGED_WEBSPACE - entity BI_DOMAIN_DNS_SETUP - entity BI_DOMAIN_SMTP_SETUP } package Hosting #feb28c{ package MariaDB #99bcdb { - entity HA_MARIADB_INSTANCE - entity HA_MARIADB_USER + entity HA_MARIADB_INSTANCE + entity HA_MARIADB_USER entity HA_MARIADB_DATABASE } package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER entity HA_IP_NUMBER } package Webspace #99bcdb { - entity HA_MANAGED_WEBSPACE - entity HA_UNIX_USER + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER entity HA_EMAIL_ALIAS } @@ -110,16 +109,16 @@ BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER -HA_CLOUD_SERVER ==* BI_CLOUD_SERVER -HA_MANAGED_SERVER ==* BI_MANAGED_SERVER -HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE +HA_CLOUD_SERVER *==> BI_CLOUD_SERVER +HA_MANAGED_SERVER *==> BI_MANAGED_SERVER +HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER HA_UNIX_USER *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER -HA_MARIADB_USER *==> HA_MARIADB_INSTANCE -HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE -HA_MARIADB_DATABASE *==> HA_MANAGED_WEBSPACE +HA_MARIADB_USER *==> HA_MANAGED_WEBSPACE +HA_MARIADB_USER o..> HA_MARIADB_INSTANCE +HA_MARIADB_DATABASE *==> HA_MARIADB_USER HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER @@ -133,6 +132,7 @@ package Legend #white { } Booking -down[hidden]->Legend ``` + ### PostgreSQL ```plantuml @@ -144,26 +144,24 @@ package Booking #feb28c { entity BI_CLOUD_SERVER entity BI_MANAGED_SERVER entity BI_MANAGED_WEBSPACE - entity BI_DOMAIN_DNS_SETUP - entity BI_DOMAIN_SMTP_SETUP } package Hosting #feb28c{ package PostgreSQL #99bcdb { - entity HA_PGSQL_INSTANCE - entity HA_PGSQL_USER + entity HA_PGSQL_INSTANCE + entity HA_PGSQL_USER entity HA_PGSQL_DATABASE } package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER entity HA_IP_NUMBER } package Webspace #99bcdb { - entity HA_MANAGED_WEBSPACE - entity HA_UNIX_USER + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER entity HA_EMAIL_ALIAS } @@ -173,9 +171,9 @@ BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER -HA_CLOUD_SERVER ==* BI_CLOUD_SERVER -HA_MANAGED_SERVER ==* BI_MANAGED_SERVER -HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE +HA_CLOUD_SERVER *==> BI_CLOUD_SERVER +HA_MANAGED_SERVER *==> BI_MANAGED_SERVER +HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER HA_UNIX_USER *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE @@ -196,4 +194,5 @@ package Legend #white { } Booking -down[hidden]->Legend ``` - This code generated was by HsHostingAssetType.main, do not amend manually. + +This code generated was by HsHostingAssetType.main, do not amend manually. 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 669c4ca1..bf9563f8 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 @@ -100,13 +100,13 @@ public enum HsHostingAssetType implements Node { MARIADB_USER( // named e.g. xyz00_abc inGroup("MariaDB"), - requiredParent(MARIADB_INSTANCE), - assignedTo(MANAGED_WEBSPACE)), + requiredParent(MANAGED_WEBSPACE), // thus, the MANAGED_WEBSPACE:Agent becomes RBAC owner + assignedTo(MARIADB_INSTANCE)), // keep in mind: no RBAC grants implied MARIADB_DATABASE( // named e.g. xyz00_abc inGroup("MariaDB"), - requiredParent(MARIADB_INSTANCE), // TODO.spec: or MARIADB_USER? - assignedTo(MARIADB_USER)), // TODO.spec: or swapping parent+assignedTo? + requiredParent(MARIADB_USER), // thus, the MARIADB_USER:Agent becomes RBAC owner + assignedTo(MARIADB_INSTANCE)), // keep in mind: no RBAC grants implied IP_NUMBER( inGroup("Server"), 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 593556e6..c183ffbc 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 @@ -14,13 +14,12 @@ class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator MARIADB_DATABASE, AlarmContact.isOptional(), - stringProperty("encoding").matchesRegEx("[a-z0-9_]+").maxLength(24).provided("latin2", "utf8").withDefault("utf8"), - stringProperty("collation").matchesRegEx("[a-z0-9_]+").maxLength(24).provided("latin2", "utf8").withDefault("utf8")); + stringProperty("encoding").matchesRegEx("[a-z0-9_]+").maxLength(24).provided("latin1", "utf8").withDefault("utf8")); } @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { - final var webspaceIdentifier = assetEntity.getAssignedToAsset().getAssignedToAsset().getIdentifier(); + final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9]+$"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsMariaDbUserHostingAssetValidator.java index 0b99e56c..9ee6af9a 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 @@ -27,7 +27,7 @@ class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator { @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { - final var webspaceIdentifier = assetEntity.getAssignedToAsset().getIdentifier(); + final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9]+$"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java index 8bbe7365..2b17d85b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java @@ -36,13 +36,13 @@ class HsHostingAssetTypeUnitTest { entity HA_DOMAIN_MBOX_SETUP entity HA_EMAIL_ADDRESS } - + package Server #99bcdb { entity HA_CLOUD_SERVER entity HA_MANAGED_SERVER entity HA_IP_NUMBER } - + package Webspace #99bcdb { entity HA_MANAGED_WEBSPACE entity HA_UNIX_USER @@ -129,10 +129,10 @@ class HsHostingAssetTypeUnitTest { HA_UNIX_USER *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER - HA_MARIADB_USER *==> HA_MARIADB_INSTANCE - HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE - HA_MARIADB_DATABASE *==> HA_MARIADB_INSTANCE - HA_MARIADB_DATABASE o..> HA_MARIADB_USER + HA_MARIADB_USER *==> HA_MANAGED_WEBSPACE + HA_MARIADB_USER o..> HA_MARIADB_INSTANCE + HA_MARIADB_DATABASE *==> HA_MARIADB_USER + HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE @@ -145,7 +145,7 @@ class HsHostingAssetTypeUnitTest { } Booking -down[hidden]->Legend ``` - + ### PostgreSQL ```plantuml 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 c75a135f..c5459b31 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 @@ -27,8 +27,8 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { private static final HsHostingAssetEntity GIVEN_MARIADB_USER = HsHostingAssetEntity.builder() .type(MARIADB_USER) - .parentAsset(GIVEN_MARIADB_INSTANCE) - .assignedToAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) + .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) + .assignedToAsset(GIVEN_MARIADB_INSTANCE) .identifier("xyz00_temp") .caption("some valid test MariaDB-User") .config(new HashMap<>(ofEntries( @@ -39,13 +39,12 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { private static HsHostingAssetEntityBuilder givenValidMariaDbDatabaseBuilder() { return HsHostingAssetEntity.builder() .type(MARIADB_DATABASE) - .parentAsset(GIVEN_MARIADB_INSTANCE) - .assignedToAsset(GIVEN_MARIADB_USER) + .parentAsset(GIVEN_MARIADB_USER) + .assignedToAsset(GIVEN_MARIADB_INSTANCE) .identifier("xyz00_temp") .caption("some valid test MariaDB-Database") .config(new HashMap<>(ofEntries( - entry("encoding", "latin1"), - entry("collation", "latin1") + entry("encoding", "latin1") ))); } @@ -59,8 +58,7 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { // then assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( - "{type=string, propertyName=encoding, matchesRegEx=[[a-z0-9_]+], maxLength=24, provided=[latin2, utf8], defaultValue=utf8}", - "{type=string, propertyName=collation, matchesRegEx=[[a-z0-9_]+], maxLength=24, provided=[latin2, utf8], defaultValue=utf8}" + "{type=string, propertyName=encoding, matchesRegEx=[[a-z0-9_]+], maxLength=24, provided=[latin1, utf8], defaultValue=utf8}" ); } @@ -86,8 +84,7 @@ class HsMariaDbDatabaseHostingAssetValidatorUnitTest { final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder() .config(ofEntries( entry("unknown", "wrong"), - entry("encoding", 10), - entry("collation", 20) + entry("encoding", 10) )) .build(); final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType()); @@ -98,8 +95,7 @@ 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:xyz00_temp.config.collation' is expected to be of type String, but is of type Integer" + "'MARIADB_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" ); } 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 7406a6fe..7ef55796 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 @@ -27,8 +27,8 @@ class HsMariaDbUserHostingAssetValidatorUnitTest { private static HsHostingAssetEntityBuilder givenValidMariaDbUserBuilder() { return HsHostingAssetEntity.builder() .type(MARIADB_USER) - .parentAsset(GIVEN_MARIADB_INSTANCE) - .assignedToAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) + .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) + .assignedToAsset(GIVEN_MARIADB_INSTANCE) .identifier("xyz00_temp") .caption("some valid test MariaDB-User") .config(new HashMap<>(ofEntries( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java index 61cebf95..0d128cab 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsUnixUserHostingAssetValidatorUnitTest.java @@ -169,7 +169,7 @@ class HsUnixUserHostingAssetValidatorUnitTest { "{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}", "{type=string, propertyName=homedir, readOnly=true, computed=true}", "{type=string, propertyName=totpKey, matchesRegEx=[^0x([0-9A-Fa-f]{2})+$], minLength=20, maxLength=256, writeOnly=true, undisclosed=true}", - "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}" + "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=LINUX_SHA512, undisclosed=true}" ); } } -- 2.39.5 From b169aff6aff5a233938cf3a0beac3eabe8128794 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 12 Jul 2024 10:54:15 +0200 Subject: [PATCH 6/6] amendments after code-review --- .../asset/validators/HsUnixUserHostingAssetValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6a4299f1..7bcbb028 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 @@ -31,7 +31,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator { stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir), stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(), passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly()); - // TODO.spec: private SSH keys? + // TODO.spec: public SSH keys? } @Override -- 2.39.5