From 0f070ab8bbb054be17c4300a26535c40056d867e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 30 Aug 2024 17:18:05 +0200 Subject: [PATCH 1/6] create webspace main user for new webspaces --- .../hs/hosting/asset/HsHostingAsset.java | 8 +++++ .../HostingAssetEntitySaveProcessor.java | 2 ++ ...sManagedWebspaceHostingAssetValidator.java | 31 +++++++++++++++++++ .../hs/validation/HsEntityValidator.java | 3 ++ ...sHostingAssetControllerAcceptanceTest.java | 22 ++++++++++--- 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java index 53e3f992..abe56085 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java @@ -34,6 +34,7 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.PostLoad; import jakarta.persistence.Transient; import jakarta.persistence.Version; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -115,6 +116,13 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity getSubHostingAssets() { + if (subHostingAssets == null) { + subHostingAssets = new ArrayList<>(); + } + return subHostingAssets; + } + public PatchableMapWrapper getConfig() { return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java index c5951f45..09628919 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java @@ -66,6 +66,8 @@ public class HostingAssetEntitySaveProcessor { public HostingAssetEntitySaveProcessor saveUsing(final Function saveFunction) { step("saveUsing", "validateContext"); entity = saveFunction.apply(entity); + em.flush(); // makes RbacEntity available as RealEntity if needed + validator.postPersist(em, entity); return this; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index dc0ece36..ca10ee7c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -1,10 +1,13 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity; +import jakarta.persistence.EntityManager; import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator { public HsManagedWebspaceHostingAssetValidator() { @@ -22,4 +25,32 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator : "[a-z][a-z0-9][a-z0-9]"; return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$"); } + + @Override + public void postPersist(final EntityManager em, final HsHostingAsset webspaceAsset) { + if (!webspaceAsset.isLoaded()) { + final var unixUserAsset = HsHostingAssetRealEntity.builder() + .type(UNIX_USER) + .parentAsset(em.find(HsHostingAssetRealEntity.class, webspaceAsset.getUuid())) + .identifier(webspaceAsset.getIdentifier()) + .caption(webspaceAsset.getIdentifier() + " webspace user") + .build(); + webspaceAsset.getSubHostingAssets().add(unixUserAsset); + final var emw = new EntityManagerWrapper(em); + new HostingAssetEntitySaveProcessor(em, unixUserAsset) + .preprocessEntity() + .validateEntity() + .prepareForSave() + .saveUsing(emw::persist) + .validateContext(); + } + } +} + +record EntityManagerWrapper(EntityManager em) { + + HsHostingAsset persist(HsHostingAsset hsHostingAsset) { + em.persist(hsHostingAsset); + return hsHostingAsset; + } } 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 ce353a7d..b2fa8a02 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -160,4 +160,7 @@ public abstract class HsEntityValidator { public ValidatableProperty getProperty(final String propertyName) { return stream(propertyValidators).filter(pv -> pv.propertyName().equals(propertyName)).findFirst().orElse(null); } + + public void postPersist(final EntityManager em, final E entity) { + } } 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 9f689591..9647166b 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 @@ -182,11 +182,23 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*")) .extract().header("Location"); // @formatter:on - // finally, the new asset can be accessed under the generated UUID - final var newWebspace = UUID.fromString( + // the new asset can be accessed under the generated UUID + final var newWebspaceUuid = UUID.fromString( location.substring(location.lastIndexOf('/') + 1)); - assertThat(newWebspace).isNotNull(); - toCleanup(HsHostingAssetRbacEntity.class, newWebspace); + assertThat(newWebspaceUuid).isNotNull(); + toCleanup(HsHostingAssetRbacEntity.class, newWebspaceUuid); + + // and a default user got created + final var webspaceUnixUser = em.createQuery("SELECT ha FROM HsHostingAssetRealEntity ha WHERE ha.parentAsset.uuid=:webspaceUUID") + .setParameter("webspaceUUID", newWebspaceUuid) + .getSingleResult(); + assertThat(webspaceUnixUser).isNotNull().extracting(Object::toString) + .isEqualTo(""" + HsHostingAsset(UNIX_USER, fir10, fir10 webspace user, MANAGED_WEBSPACE:fir10, { + "password" : null, + "userid" : 1000000 + }) + """.trim()); } @Test @@ -325,7 +337,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup jpaAttempt.transacted(() -> { context.define("superuser-alex@hostsharing.net"); - for (int n = 0; n < UNIX_USER_PER_MULTI_OPTION -preExistingUnixUserCount+1; ++n) { + for (int n = 0; n < UNIX_USER_PER_MULTI_OPTION-preExistingUnixUserCount; ++n) { toCleanup(realAssetRepo.save( HsHostingAssetRealEntity.builder() .type(UNIX_USER) -- 2.39.5 From 8ae270f887233ffdef1f561cbc9d859b30d07505 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 30 Aug 2024 17:32:50 +0200 Subject: [PATCH 2/6] set webspace groupid to main user userid --- .../validators/HsManagedWebspaceHostingAssetValidator.java | 5 ++++- .../asset/HsHostingAssetControllerAcceptanceTest.java | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index ca10ee7c..52a29dc5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -8,13 +8,15 @@ import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; +import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator { public HsManagedWebspaceHostingAssetValidator() { super( MANAGED_WEBSPACE, AlarmContact.isOptional(), - NO_EXTRA_PROPERTIES); // TODO.impl: groupid missing, should be equal to main user + integerProperty("groupid").readOnly() + ); } @Override @@ -43,6 +45,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator .prepareForSave() .saveUsing(emw::persist) .validateContext(); + webspaceAsset.getConfig().put("groupid", unixUserAsset.getConfig().get("userid")); } } } 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 9647166b..c668c6c9 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 @@ -176,7 +176,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup "type": "MANAGED_WEBSPACE", "identifier": "fir10", "caption": "some separate ManagedWebspace HA", - "config": {} + "config": { + "groupid": 1000000 + } } """)) .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*")) -- 2.39.5 From 7dc7991b0e51d76382ae3cafece37c778be1812d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 30 Aug 2024 18:36:18 +0200 Subject: [PATCH 3/6] also import groupid --- .../hs/migration/ImportHostingAssets.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) 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 4b7375f9..677d808b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -350,6 +350,18 @@ public class ImportHostingAssets extends BaseOfficeDataImport { 9596=HsHostingAsset(UNIX_USER, dph00-dph, Domain admin, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) } """); + + // now with groupids + assertThat(firstOfEach(5, packetAssets, MANAGED_WEBSPACE)) + .isEqualToIgnoringWhitespace(""" + { + 10630=HsHostingAsset(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00, {"groupid": 6824}), + 11094=HsHostingAsset(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00, {"groupid": 5803}), + 11111=HsHostingAsset(MANAGED_WEBSPACE, xyz68, HA xyz68, MANAGED_SERVER:vm1068, D-1000000:vm1068 Monitor:BI xyz68, {"groupid": 5961}), + 11112=HsHostingAsset(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00, {"groupid": 5964}), + 19959=HsHostingAsset(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00, {"groupid": 9546}) + } + """); } @Test @@ -1235,9 +1247,10 @@ public class ImportHostingAssets extends BaseOfficeDataImport { .forEach(rec -> { final var unixuser_id = rec.getInteger("unixuser_id"); final var packet_id = rec.getInteger("packet_id"); + final var parentWebspaceAsset = packetAssets.get(packet_id); final var unixUserAsset = HsHostingAssetRealEntity.builder() .type(UNIX_USER) - .parentAsset(packetAssets.get(packet_id)) + .parentAsset(parentWebspaceAsset) .identifier(rec.getString("name")) .caption(rec.getString("comment")) .isLoaded(true) // avoid overwriting imported userids with generated ids @@ -1253,6 +1266,10 @@ public class ImportHostingAssets extends BaseOfficeDataImport { ))) .build(); + if (unixUserAsset.getIdentifier().equals(parentWebspaceAsset.getIdentifier())) { + parentWebspaceAsset.getConfig().put("groupid", unixuser_id); + } + // TODO.spec: crop SSD+HDD limits if > booked if (unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0) > 1024 * unixUserAsset.getContextValue("SSD", Integer.class, 0)) { -- 2.39.5 From 1cf192a7f081c03f6b752180de4132832e496144 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 2 Sep 2024 16:12:54 +0200 Subject: [PATCH 4/6] refactoring --- .../hsadminng/hs/hosting/asset/HsHostingAsset.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java index abe56085..52e884e1 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAsset.java @@ -89,9 +89,10 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity subHostingAssets; + private List subHostingAssets = new ArrayList<>(); @Column(name = "identifier") private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc @@ -116,13 +117,6 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity getSubHostingAssets() { - if (subHostingAssets == null) { - subHostingAssets = new ArrayList<>(); - } - return subHostingAssets; - } - public PatchableMapWrapper getConfig() { return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); } -- 2.39.5 From 75c3c6f71687462f399b24b0f6268131e075a8c9 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 3 Sep 2024 09:23:00 +0200 Subject: [PATCH 5/6] introduce HostingAssetEntitySaveProcessor.save using the EntityManager --- .../asset/HsHostingAssetController.java | 4 +-- .../HostingAssetEntitySaveProcessor.java | 31 ++++++++++++++++--- ...sManagedWebspaceHostingAssetValidator.java | 2 +- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java index 4ae94c00..cb4e3446 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java @@ -79,7 +79,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { .preprocessEntity() .validateEntity() .prepareForSave() - .saveUsing(rbacAssetRepo::save) + .save() .validateContext() .mapUsing(e -> mapper.map(e, HsHostingAssetResource.class)) .revampProperties(); @@ -140,7 +140,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { .preprocessEntity() .validateEntity() .prepareForSave() - .saveUsing(rbacAssetRepo::save) + .save() .validateContext() .mapUsing(e -> mapper.map(e, HsHostingAssetResource.class)) .revampProperties(); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java index 09628919..3e5850e5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntitySaveProcessor.java @@ -58,19 +58,42 @@ public class HostingAssetEntitySaveProcessor { /// hashing passwords etc. @SuppressWarnings("unchecked") public HostingAssetEntitySaveProcessor prepareForSave() { - step("prepareForSave", "saveUsing"); + step("prepareForSave", "save"); validator.prepareProperties(em, entity); return this; } + /** + * Saves the entity using the given `saveFunction`. + * + *

`validator.postPersist(em, entity)` is NOT called. + * If any postprocessing is necessary, the saveFunction has to implement this.

+ * @param saveFunction + * @return + */ public HostingAssetEntitySaveProcessor saveUsing(final Function saveFunction) { - step("saveUsing", "validateContext"); + step("save", "validateContext"); entity = saveFunction.apply(entity); - em.flush(); // makes RbacEntity available as RealEntity if needed - validator.postPersist(em, entity); return this; } + /** + * Saves the using the `EntityManager`, but does NOT ever merge the entity. + * + *

`validator.postPersist(em, entity)` is called afterwards with the entity guaranteed to be flushed to the database.

+ * @return + */ + public HostingAssetEntitySaveProcessor save() { + return saveUsing(e -> { + if (!em.contains(entity)) { + em.persist(entity); + } + em.flush(); // makes RbacEntity available as RealEntity if needed + validator.postPersist(em, entity); + return entity; + }); + } + /// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits) public HostingAssetEntitySaveProcessor validateContext() { step("validateContext", "mapUsing"); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index 52a29dc5..d39b052b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -43,7 +43,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator .preprocessEntity() .validateEntity() .prepareForSave() - .saveUsing(emw::persist) + .save() .validateContext(); webspaceAsset.getConfig().put("groupid", unixUserAsset.getConfig().get("userid")); } -- 2.39.5 From f2f43b9ebab30855d8bcb2078cd229e2cb028351 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 3 Sep 2024 10:28:06 +0200 Subject: [PATCH 6/6] cleanup after code-review --- .../HsManagedWebspaceHostingAssetValidator.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index d39b052b..4579faf8 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -38,7 +38,6 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator .caption(webspaceAsset.getIdentifier() + " webspace user") .build(); webspaceAsset.getSubHostingAssets().add(unixUserAsset); - final var emw = new EntityManagerWrapper(em); new HostingAssetEntitySaveProcessor(em, unixUserAsset) .preprocessEntity() .validateEntity() @@ -49,11 +48,3 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator } } } - -record EntityManagerWrapper(EntityManager em) { - - HsHostingAsset persist(HsHostingAsset hsHostingAsset) { - em.persist(hsHostingAsset); - return hsHostingAsset; - } -} -- 2.39.5