From de4870621a6966d644e97f0b6d510c5b096e52fa Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 5 Jul 2024 16:57:30 +0200 Subject: [PATCH 1/9] add Mermaid graph generator for HostingAssetType structure (WIP) --- doc/hs-hosting-asset-type-structure.md | 77 ++++++ .../hs/booking/item/HsBookingItemType.java | 37 ++- .../hsadminng/hs/booking/item/Node.java | 9 + ...HsManagedWebspaceBookingItemValidator.java | 4 +- .../hs/hosting/asset/HsHostingAssetType.java | 243 ++++++++++++++++-- ...HsDomainDnsSetupHostingAssetValidator.java | 2 +- ...HsHostingAssetEntityValidatorRegistry.java | 1 - ...gedServerBookingItemValidatorUnitTest.java | 2 +- .../HsHostingAssetControllerRestTest.java | 18 -- ...HostingAssetRepositoryIntegrationTest.java | 2 - ...DnsSetupHostingAssetValidatorUnitTest.java | 2 - ...ainSetupHostingAssetValidatorUnitTest.java | 111 -------- ...gAssetEntityValidatorRegistryUnitTest.java | 1 - 13 files changed, 346 insertions(+), 163 deletions(-) create mode 100644 doc/hs-hosting-asset-type-structure.md create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/booking/item/Node.java delete mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md new file mode 100644 index 00000000..b80d1814 --- /dev/null +++ b/doc/hs-hosting-asset-type-structure.md @@ -0,0 +1,77 @@ +### HostingAsset Type Structure + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart RL + +subgraph Booking + style Booking fill:white,stroke:white,stroke-width:0px + BI:PRIVATE_CLOUD + BI:CLOUD_SERVER + BI:MANAGED_SERVER + BI:MANAGED_WEBSPACE + BI:DOMAIN_DNS_SETUP + BI:DOMAIN_EMAIL_SUBMISSION_SETUP +end + +%% subgraph Hosting +subgraph Server[ ] + style Server fill:white,stroke:white,stroke-width:0px +HA:CLOUD_SERVER +HA:MANAGED_SERVER +HA:IP_NUMBER +end + +subgraph Domain[ ] + style Domain fill:white,stroke:white,stroke-width:0px +HA:DOMAIN_DNS_SETUP +HA:DOMAIN_HTTP_SETUP +HA:DOMAIN_EMAIL_SUBMISSION_SETUP +HA:DOMAIN_EMAIL_MAILBOX_SETUP +HA:EMAIL_ADDRESS +end + +subgraph Webspace[ ] + style Webspace fill:white,stroke:white,stroke-width:0px +HA:MANAGED_WEBSPACE +HA:UNIX_USER +HA:EMAIL_ALIAS +end + +%% end + +style HA:CLOUD_SERVER fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:MANAGED_SERVER fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:MANAGED_WEBSPACE fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:UNIX_USER fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:DOMAIN_DNS_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:DOMAIN_HTTP_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:DOMAIN_EMAIL_SUBMISSION_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:DOMAIN_EMAIL_MAILBOX_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:EMAIL_ALIAS fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:EMAIL_ADDRESS fill:#99bcdb,stroke:#274d6e,stroke-width:4px +style HA:IP_NUMBER fill:#99bcdb,stroke:#274d6e,stroke-width:4px + +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:MANAGED_WEBSPACE ==o HA:MANAGED_SERVER +HA:UNIX_USER ==> HA:MANAGED_WEBSPACE +HA:DOMAIN_DNS_SETUP ==> BI:DOMAIN_DNS_SETUP +HA:DOMAIN_HTTP_SETUP ==> HA:MANAGED_WEBSPACE +HA:DOMAIN_HTTP_SETUP -.-> HA:UNIX_USER +HA:DOMAIN_EMAIL_SUBMISSION_SETUP ==> BI:DOMAIN_EMAIL_SUBMISSION_SETUP +HA:DOMAIN_EMAIL_SUBMISSION_SETUP -.-o HA:MANAGED_WEBSPACE +HA:DOMAIN_EMAIL_MAILBOX_SETUP ==> HA:MANAGED_WEBSPACE +HA:EMAIL_ALIAS ==> HA:MANAGED_WEBSPACE +HA:EMAIL_ADDRESS ==> HA:DOMAIN_EMAIL_MAILBOX_SETUP +HA:IP_NUMBER -.-> HA:CLOUD_SERVER +HA:IP_NUMBER -.-> HA:MANAGED_SERVER +HA:IP_NUMBER -.-> HA:MANAGED_WEBSPACE +``` + +This code generated was by HsHostingAssetType.main, do not amend manually. diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java index 719ce75b..6cb9c34f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java @@ -1,8 +1,37 @@ package net.hostsharing.hsadminng.hs.booking.item; -public enum HsBookingItemType { +import java.util.List; + +import static java.util.Optional.ofNullable; + +public enum HsBookingItemType implements Node { PRIVATE_CLOUD, - CLOUD_SERVER, - MANAGED_SERVER, - MANAGED_WEBSPACE + CLOUD_SERVER(PRIVATE_CLOUD), + MANAGED_SERVER(PRIVATE_CLOUD), + MANAGED_WEBSPACE(MANAGED_SERVER), + DOMAIN_DNS_SETUP, // TODO.spec: experimental + DOMAIN_EMAIL_SUBMISSION_SETUP; // TODO.spec: experimental + + private final HsBookingItemType parentItemType; + + HsBookingItemType() { + this.parentItemType = null; + } + + HsBookingItemType(final HsBookingItemType parentItemType) { + this.parentItemType = parentItemType; + } + + @Override + public List edges() { + return ofNullable(parentItemType) + .map(p -> (nodeName() + " --> " + p.nodeName())) + .stream().toList(); + } + + @Override + public String nodeName() { + return "BI:" + name(); + } + } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/Node.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/Node.java new file mode 100644 index 00000000..cca14f5a --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/Node.java @@ -0,0 +1,9 @@ +package net.hostsharing.hsadminng.hs.booking.item; + +import java.util.List; + +public interface Node { + + String nodeName(); + List edges(); +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java index 81c74b9f..2bca0042 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java @@ -8,7 +8,7 @@ import java.util.List; import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_EMAIL_SETUP; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.EMAIL_ADDRESS; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_DATABASE; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER; @@ -88,7 +88,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = ofNullable(entity.getRelatedHostingAsset()) .map(ha -> ha.getSubHostingAssets().stream() - .filter(bi -> bi.getType() == DOMAIN_EMAIL_SETUP) + .filter(bi -> bi.getType() == DOMAIN_EMAIL_MAILBOX_SETUP) .flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream() .filter(subAsset -> subAsset.getType()==EMAIL_ADDRESS)) .count()) 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 88ccca45..5ae68f21 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 @@ -1,33 +1,131 @@ package net.hostsharing.hsadminng.hs.hosting.asset; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.booking.item.Node; -public enum HsHostingAssetType { - CLOUD_SERVER, // named e.g. vm1234 - MANAGED_SERVER, // named e.g. vm1234 - MANAGED_WEBSPACE(MANAGED_SERVER), // named eg. xyz00 - UNIX_USER(MANAGED_WEBSPACE), // named e.g. xyz00-abc - DOMAIN_SETUP, // named e.g. example.org - DOMAIN_DNS_SETUP(DOMAIN_SETUP), // named e.g. example.org - DOMAIN_HTTP_SETUP(DOMAIN_SETUP), // named e.g. example.org - DOMAIN_EMAIL_SETUP(DOMAIN_SETUP), // named e.g. example.org +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.joining; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; + +public enum HsHostingAssetType implements Node { + CLOUD_SERVER( // named e.g. vm1234 + inGroup("Server"), + requires(HsBookingItemType.CLOUD_SERVER)), + + MANAGED_SERVER( // named e.g. vm1234 + inGroup("Server"), + requires(HsBookingItemType.MANAGED_SERVER)), + + MANAGED_WEBSPACE( // named eg. xyz00 + inGroup("Webspace"), + requires(HsBookingItemType.MANAGED_WEBSPACE), + optionalParent(MANAGED_SERVER)), + + UNIX_USER( // named e.g. xyz00-abc + inGroup("Webspace"), + requiredParent(MANAGED_WEBSPACE)), + +// DOMAIN_SETUP( // named e.g. example.org +// inGroup("Domain") +// ), + + DOMAIN_DNS_SETUP( // named e.g. example.org + inGroup("Domain"), + requires(HsBookingItemType.DOMAIN_DNS_SETUP)), + + DOMAIN_HTTP_SETUP( // named e.g. example.org + inGroup("Domain"), + requiredParent(MANAGED_WEBSPACE), + assignedTo(UNIX_USER)), + + DOMAIN_EMAIL_SUBMISSION_SETUP( // named e.g. example.org + inGroup("Domain"), + requires(HsBookingItemType.DOMAIN_EMAIL_SUBMISSION_SETUP), + optionallyAssignedTo(MANAGED_WEBSPACE)), + + DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org + inGroup("Domain"), + requiredParent(MANAGED_WEBSPACE)), // TODO.spec: SECURE_MX - EMAIL_ALIAS(MANAGED_WEBSPACE), // named e.g. xyz00-abc - EMAIL_ADDRESS(DOMAIN_EMAIL_SETUP), // named e.g. sample@example.org - PGSQL_USER(MANAGED_WEBSPACE), // named e.g. xyz00_abc - PGSQL_DATABASE(MANAGED_WEBSPACE), // named e.g. xyz00_abc, TODO.spec: or PGSQL_USER? - MARIADB_USER(MANAGED_WEBSPACE), // named e.g. xyz00_abc - MARIADB_DATABASE(MANAGED_WEBSPACE); // named e.g. xyz00_abc, TODO.spec: or MARIADB_USER? + EMAIL_ALIAS( // named e.g. xyz00-abc + inGroup("Webspace"), + requiredParent(MANAGED_WEBSPACE)), - public final HsHostingAssetType parentAssetType; + EMAIL_ADDRESS( // named e.g. sample@example.org + inGroup("Domain"), + requiredParent(DOMAIN_EMAIL_MAILBOX_SETUP)), - HsHostingAssetType(final HsHostingAssetType parentAssetType) { - this.parentAssetType = parentAssetType; + PGSQL_INSTANCE( // TODO.spec: identifier to be specified + inGroup("PostgreSQL"), + requiredParent(MANAGED_SERVER)), + + PGSQL_USER( // named e.g. xyz00_abc + inGroup("PostgreSQL"), + requiredParent(PGSQL_INSTANCE), + assignedTo(MANAGED_WEBSPACE)), + + PGSQL_DATABASE( // named e.g. xyz00_abc + inGroup("PostgreSQL"), + requiredParent(MANAGED_WEBSPACE), // TODO.spec: or PGSQL_USER? + assignedTo(PGSQL_INSTANCE)), // TODO.spec: or swapping parent+assignedTo? + + MARIADB_INSTANCE( // TODO.spec: identifier to be specified + inGroup("MariaDB"), + requiredParent(MANAGED_SERVER)), // TODO.spec: or MANAGED_WEBSPACE? + + MARIADB_USER( // named e.g. xyz00_abc + inGroup("MariaDB"), + requiredParent(MARIADB_INSTANCE), + assignedTo(MANAGED_WEBSPACE)), + + 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? + + IP_NUMBER( + inGroup("Server"), + assignedTo(CLOUD_SERVER), + assignedTo(MANAGED_SERVER), + assignedTo(MANAGED_WEBSPACE) + ); + + private final String groupName; + private final EntityTypeRelation[] relations; + + HsHostingAssetType( + final String groupName, + final EntityTypeRelation... relations + ) { + this.groupName = groupName; + this.relations = relations; } - HsHostingAssetType() { - this(null); + /// just syntactic sugar + private static String inGroup(final String groupName) { + return groupName; + } + + @Override + public List edges() { + return stream(relations) + .map(r -> nodeName() + r.edge + r.relatedType.nodeName()) + .toList(); + } + + @Override + public String nodeName() { + return "HA:" + name(); } public static > HsHostingAssetType of(final T value) { @@ -37,4 +135,109 @@ public enum HsHostingAssetType { static String asString(final HsHostingAssetType type) { return type == null ? null : type.name(); } + + public static void main(final String[] args) throws IOException { + + final var includedHostingGroups = Set.of("Domain", "Server", "Webspace"); + + final String bookingNodes = stream(HsBookingItemType.values()) + .map(t -> " " + t.nodeName()) + .collect(joining("\n")); + final String hostingGroups = includedHostingGroups.stream() + .map(HsHostingAssetType::generateGroup) + .collect(joining("\n")); + final String hostingAssetNodeStyles = stream(HsHostingAssetType.values()) + .filter(t -> includedHostingGroups.contains(t.groupName)) + .map(n -> "style HA:"+n.name()+" fill:#99bcdb,stroke:#274d6e,stroke-width:4px") + .collect(joining("\n")); + final String bookingItemEdges = stream(HsBookingItemType.values()) + .map(HsBookingItemType::edges) + .flatMap(Collection::stream) + .collect(joining("\n")); + final String hostingAssetEdges = stream(HsHostingAssetType.values()) + .filter(t -> includedHostingGroups.contains(t.groupName)) + .map(HsHostingAssetType::edges) + .flatMap(Collection::stream) + .collect(joining("\n")); + Files.writeString( + Path.of("doc/hs-hosting-asset-type-structure.md"), + """ + ### HostingAsset Type Structure + + ```mermaid + %%{init:{'flowchart':{'htmlLabels':false}}}%% + flowchart RL + + subgraph Booking + style Booking fill:white,stroke:white,stroke-width:0px + %{bookingNodes} + end + + %% subgraph Hosting + %{hostingGroups} + %% end + + %{hostingAssetNodeStyles} + + %{bookingItemEdges} + + %{hostingAssetEdges} + ``` + + This code generated was by %{this}.main, do not amend manually. + """ + .replace("%{this}", HsHostingAssetType.class.getSimpleName()) + .replace("%{bookingNodes}", bookingNodes) + .replace("%{hostingGroups}", hostingGroups) + .replace("%{hostingAssetNodeStyles}", hostingAssetNodeStyles) + .replace("%{bookingItemEdges}", bookingItemEdges) + .replace("%{hostingAssetEdges}", hostingAssetEdges), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + private static String generateGroup(final String group) { + return "subgraph " + group + "[ ]\n" + + " style " + group + " fill:white,stroke:white,stroke-width:0px\n" + + stream(HsHostingAssetType.values()) + .filter(t -> t.groupName.equals(group)) + .map(HsHostingAssetType::nodeName) + .collect(joining(" \n")) + + "\nend\n"; + } +} + +enum TypeRelationType { + OPTIONAL, REQUIRED +} + +class EntityTypeRelation { + final String edge; + final TypeRelationType relationType; + final T relatedType; + + EntityTypeRelation(final String edge, final TypeRelationType required, final T relatedType) { + this.edge = edge; + this.relationType = required; + this.relatedType = relatedType; + } + + static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { + return new EntityTypeRelation<>(" ==> ", TypeRelationType.REQUIRED, bookingItemType); + } + + static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(" ==o ", TypeRelationType.OPTIONAL, hostingAssetType); + } + + static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(" ==> ", TypeRelationType.REQUIRED, hostingAssetType); + } + + static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(" -.-> ", TypeRelationType.REQUIRED, hostingAssetType); + } + + static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(" -.-o ", TypeRelationType.OPTIONAL, hostingAssetType); + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index e09f77ef..3205a0da 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -33,7 +33,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato HsDomainDnsSetupHostingAssetValidator() { super( BookingItem.mustBeNull(), - ParentAsset.mustBeOfType(HsHostingAssetType.DOMAIN_SETUP), + ParentAsset.mustBeNull(), AssignedToAsset.mustBeNull(), AlarmContact.isOptional(), diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java index 3ae14256..432dee9a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java @@ -20,7 +20,6 @@ public class HsHostingAssetEntityValidatorRegistry { register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator()); register(UNIX_USER, new HsUnixUserHostingAssetValidator()); register(EMAIL_ALIAS, new HsEMailAliasHostingAssetValidator()); - register(DOMAIN_SETUP, new HsDomainSetupHostingAssetValidator()); register(DOMAIN_DNS_SETUP, new HsDomainDnsSetupHostingAssetValidator()); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java index 11020d92..b0605239 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java @@ -152,7 +152,7 @@ class HsManagedServerBookingItemValidatorUnitTest { "xyz00_%c%c", 2, HsHostingAssetType.MARIADB_DATABASE ), - generateDomainEmailSetupsWithEMailAddresses(26, HsHostingAssetType.DOMAIN_EMAIL_SETUP, + generateDomainEmailSetupsWithEMailAddresses(26, HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_SETUP, "%c%c.example.com", 10, HsHostingAssetType.EMAIL_ADDRESS ) 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 eed85585..8cd2eb3f 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 @@ -186,24 +186,6 @@ public class HsHostingAssetControllerRestTest { } ] """), - DOMAIN_SETUP( - List.of( - HsHostingAssetEntity.builder() - .type(HsHostingAssetType.DOMAIN_SETUP) - .identifier("example.org") - .caption("some fake Domain-Setup") - .build()), - """ - [ - { - "type": "DOMAIN_SETUP", - "identifier": "example.org", - "caption": "some fake Domain-Setup", - "alarmContact": null, - "config": {} - } - ] - """), DOMAIN_DNS_SETUP( List.of( HsHostingAssetEntity.builder() 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 579257a0..c1849531 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 @@ -27,7 +27,6 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf; @@ -167,7 +166,6 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu final var result = attempt(em, () -> { final var newAsset = HsHostingAssetEntity.builder() .caption("some new domain setup") - .type(DOMAIN_SETUP) .identifier("example.org") .build(); return toCleanup(assetRepo.save(newAsset)); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index 671b9452..d1ea3563 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -11,7 +11,6 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_COMMENT; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_DATA; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_TYPE; @@ -23,7 +22,6 @@ import static org.assertj.core.api.Assertions.assertThat; class HsDomainDnsSetupHostingAssetValidatorUnitTest { static final HsHostingAssetEntity validDomainSetupEntity = HsHostingAssetEntity.builder() - .type(DOMAIN_SETUP) .identifier("example.org") .build(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java deleted file mode 100644 index b7d78567..00000000 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java +++ /dev/null @@ -1,111 +0,0 @@ -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 org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -import java.util.Map; - -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; -import static org.assertj.core.api.Assertions.assertThat; - -class HsDomainSetupHostingAssetValidatorUnitTest { - - static HsHostingAssetEntityBuilder validEntityBuilder() { - return HsHostingAssetEntity.builder() - .type(DOMAIN_SETUP) - .identifier("example.org"); - } - - enum InvalidDomainNameIdentifier { - EMPTY(""), - TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"), - DASH_AT_BEGINNING("-example.com"), - DOT_AT_BEGINNING(".example.com"), - DOT_AT_END("example.com."); - - final String domainName; - - InvalidDomainNameIdentifier(final String domainName) { - this.domainName = domainName; - } - } - - @ParameterizedTest - @EnumSource(InvalidDomainNameIdentifier.class) - void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) { - // given - final var givenEntity = validEntityBuilder().identifier(testCase.domainName).build(); - final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); - - // when - final var result = validator.validateEntity(givenEntity); - - // then - assertThat(result).containsExactly( - "'identifier' expected to match '^((?!-)[A-Za-z0-9-]{1,63}(? Date: Sun, 7 Jul 2024 19:40:01 +0200 Subject: [PATCH 2/9] generate PlantUML --- doc/hs-hosting-asset-type-structure.md | 135 +++++++++--------- .../hs/booking/item/HsBookingItemType.java | 4 +- .../hs/hosting/asset/HsHostingAssetType.java | 64 +++++---- 3 files changed, 106 insertions(+), 97 deletions(-) diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md index b80d1814..ddd86373 100644 --- a/doc/hs-hosting-asset-type-structure.md +++ b/doc/hs-hosting-asset-type-structure.md @@ -1,77 +1,84 @@ ### HostingAsset Type Structure -```mermaid -%%{init:{'flowchart':{'htmlLabels':false}}}%% -flowchart RL +```plantuml +@startuml -subgraph Booking - style Booking fill:white,stroke:white,stroke-width:0px - BI:PRIVATE_CLOUD - BI:CLOUD_SERVER - BI:MANAGED_SERVER - BI:MANAGED_WEBSPACE - BI:DOMAIN_DNS_SETUP - BI:DOMAIN_EMAIL_SUBMISSION_SETUP -end +left to right direction -%% subgraph Hosting -subgraph Server[ ] - style Server fill:white,stroke:white,stroke-width:0px -HA:CLOUD_SERVER -HA:MANAGED_SERVER -HA:IP_NUMBER -end +package Booking #99bcdb { + entity BI_PRIVATE_CLOUD + entity BI_CLOUD_SERVER + entity BI_MANAGED_SERVER + entity BI_MANAGED_WEBSPACE + entity BI_DOMAIN_DNS_SETUP + entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP +} -subgraph Domain[ ] - style Domain fill:white,stroke:white,stroke-width:0px -HA:DOMAIN_DNS_SETUP -HA:DOMAIN_HTTP_SETUP -HA:DOMAIN_EMAIL_SUBMISSION_SETUP -HA:DOMAIN_EMAIL_MAILBOX_SETUP -HA:EMAIL_ADDRESS -end +package Hosting #white { + package Server #99bcdb { + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER + entity HA_IP_NUMBER + } -subgraph Webspace[ ] - style Webspace fill:white,stroke:white,stroke-width:0px -HA:MANAGED_WEBSPACE -HA:UNIX_USER -HA:EMAIL_ALIAS -end + package PostgreSQL #99bcdb { + entity HA_PGSQL_INSTANCE + entity HA_PGSQL_USER + entity HA_PGSQL_DATABASE + } -%% end + package MariaDB #99bcdb { + entity HA_MARIADB_INSTANCE + entity HA_MARIADB_USER + entity HA_MARIADB_DATABASE + } -style HA:CLOUD_SERVER fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:MANAGED_SERVER fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:MANAGED_WEBSPACE fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:UNIX_USER fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:DOMAIN_DNS_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:DOMAIN_HTTP_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:DOMAIN_EMAIL_SUBMISSION_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:DOMAIN_EMAIL_MAILBOX_SETUP fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:EMAIL_ALIAS fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:EMAIL_ADDRESS fill:#99bcdb,stroke:#274d6e,stroke-width:4px -style HA:IP_NUMBER fill:#99bcdb,stroke:#274d6e,stroke-width:4px + package Domain #99bcdb { + entity HA_DOMAIN_DNS_SETUP + entity HA_DOMAIN_HTTP_SETUP + entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP + entity HA_DOMAIN_EMAIL_MAILBOX_SETUP + entity HA_EMAIL_ADDRESS + } -BI:CLOUD_SERVER --> BI:PRIVATE_CLOUD -BI:MANAGED_SERVER --> BI:PRIVATE_CLOUD -BI:MANAGED_WEBSPACE --> BI:MANAGED_SERVER + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS + } -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:DOMAIN_DNS_SETUP ==> BI:DOMAIN_DNS_SETUP -HA:DOMAIN_HTTP_SETUP ==> HA:MANAGED_WEBSPACE -HA:DOMAIN_HTTP_SETUP -.-> HA:UNIX_USER -HA:DOMAIN_EMAIL_SUBMISSION_SETUP ==> BI:DOMAIN_EMAIL_SUBMISSION_SETUP -HA:DOMAIN_EMAIL_SUBMISSION_SETUP -.-o HA:MANAGED_WEBSPACE -HA:DOMAIN_EMAIL_MAILBOX_SETUP ==> HA:MANAGED_WEBSPACE -HA:EMAIL_ALIAS ==> HA:MANAGED_WEBSPACE -HA:EMAIL_ADDRESS ==> HA:DOMAIN_EMAIL_MAILBOX_SETUP -HA:IP_NUMBER -.-> HA:CLOUD_SERVER -HA:IP_NUMBER -.-> HA:MANAGED_SERVER -HA:IP_NUMBER -.-> HA:MANAGED_WEBSPACE +} + +BI_CLOUD_SERVER o..> BI_PRIVATE_CLOUD +BI_MANAGED_SERVER o..> BI_PRIVATE_CLOUD +BI_MANAGED_WEBSPACE o..> BI_MANAGED_SERVER + +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_DOMAIN_DNS_SETUP *==> BI_DOMAIN_DNS_SETUP +HA_DOMAIN_HTTP_SETUP *==> HA_MANAGED_WEBSPACE +HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER +HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> BI_DOMAIN_EMAIL_SUBMISSION_SETUP +HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE +HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_MANAGED_WEBSPACE +HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE +HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP +HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER +HA_PGSQL_USER *==> HA_PGSQL_INSTANCE +HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE +HA_PGSQL_DATABASE *==> HA_MANAGED_WEBSPACE +HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE +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_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 ``` This code generated was by HsHostingAssetType.main, do not amend manually. diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java index 6cb9c34f..dfc0129e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java @@ -25,13 +25,13 @@ public enum HsBookingItemType implements Node { @Override public List edges() { return ofNullable(parentItemType) - .map(p -> (nodeName() + " --> " + p.nodeName())) + .map(p -> (nodeName() + " o..> " + p.nodeName())) .stream().toList(); } @Override public String nodeName() { - return "BI:" + name(); + return "BI_" + name(); } } 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 5ae68f21..243f435b 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 @@ -9,10 +9,11 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collection; import java.util.List; -import java.util.Set; +import java.util.stream.Collectors; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toSet; import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; public enum HsHostingAssetType implements Node { @@ -100,7 +101,7 @@ public enum HsHostingAssetType implements Node { assignedTo(MANAGED_WEBSPACE) ); - private final String groupName; + final String groupName; private final EntityTypeRelation[] relations; HsHostingAssetType( @@ -125,7 +126,7 @@ public enum HsHostingAssetType implements Node { @Override public String nodeName() { - return "HA:" + name(); + return "HA_" + name(); } public static > HsHostingAssetType of(final T value) { @@ -138,17 +139,20 @@ public enum HsHostingAssetType implements Node { public static void main(final String[] args) throws IOException { - final var includedHostingGroups = Set.of("Domain", "Server", "Webspace"); + final var includedHostingGroups = stream(HsHostingAssetType.values()) + .map(t -> t.groupName) + .collect(toSet()); +// final var includedHostingGroups = Set.of("Domain", "Server", "Webspace"); final String bookingNodes = stream(HsBookingItemType.values()) - .map(t -> " " + t.nodeName()) + .map(t -> " entity " + t.nodeName()) .collect(joining("\n")); final String hostingGroups = includedHostingGroups.stream() .map(HsHostingAssetType::generateGroup) .collect(joining("\n")); - final String hostingAssetNodeStyles = stream(HsHostingAssetType.values()) - .filter(t -> includedHostingGroups.contains(t.groupName)) - .map(n -> "style HA:"+n.name()+" fill:#99bcdb,stroke:#274d6e,stroke-width:4px") + final String hostingAssetNodes = stream(HsHostingAssetType.values()) + .filter(a -> includedHostingGroups.contains(a.groupName)) + .map(a -> "entity "+a.nodeName()) .collect(joining("\n")); final String bookingItemEdges = stream(HsBookingItemType.values()) .map(HsBookingItemType::edges) @@ -164,20 +168,19 @@ public enum HsHostingAssetType implements Node { """ ### HostingAsset Type Structure - ```mermaid - %%{init:{'flowchart':{'htmlLabels':false}}}%% - flowchart RL - - subgraph Booking - style Booking fill:white,stroke:white,stroke-width:0px - %{bookingNodes} - end - - %% subgraph Hosting - %{hostingGroups} - %% end + ```plantuml + @startuml + left to right direction - %{hostingAssetNodeStyles} + package Booking #99bcdb { + %{bookingNodes} + } + + package Hosting #white { + %{hostingGroups} + } + + Booking -left-> Hosting %{bookingItemEdges} @@ -189,20 +192,19 @@ public enum HsHostingAssetType implements Node { .replace("%{this}", HsHostingAssetType.class.getSimpleName()) .replace("%{bookingNodes}", bookingNodes) .replace("%{hostingGroups}", hostingGroups) - .replace("%{hostingAssetNodeStyles}", hostingAssetNodeStyles) + .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) .replace("%{bookingItemEdges}", bookingItemEdges) .replace("%{hostingAssetEdges}", hostingAssetEdges), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } private static String generateGroup(final String group) { - return "subgraph " + group + "[ ]\n" - + " style " + group + " fill:white,stroke:white,stroke-width:0px\n" + return " package " + group + " #99bcdb {\n" + stream(HsHostingAssetType.values()) .filter(t -> t.groupName.equals(group)) - .map(HsHostingAssetType::nodeName) + .map(t -> " entity " + t.nodeName()) .collect(joining(" \n")) - + "\nend\n"; + + "\n }\n"; } } @@ -222,22 +224,22 @@ class EntityTypeRelation { } static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { - return new EntityTypeRelation<>(" ==> ", TypeRelationType.REQUIRED, bookingItemType); + return new EntityTypeRelation<>(" *==> ", TypeRelationType.REQUIRED, bookingItemType); } static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" ==o ", TypeRelationType.OPTIONAL, hostingAssetType); + return new EntityTypeRelation<>(" o..> ", TypeRelationType.OPTIONAL, hostingAssetType); } static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" ==> ", TypeRelationType.REQUIRED, hostingAssetType); + return new EntityTypeRelation<>(" *==> ", TypeRelationType.REQUIRED, hostingAssetType); } static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" -.-> ", TypeRelationType.REQUIRED, hostingAssetType); + return new EntityTypeRelation<>(" o..> ", TypeRelationType.REQUIRED, hostingAssetType); } static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" -.-o ", TypeRelationType.OPTIONAL, hostingAssetType); + return new EntityTypeRelation<>(" o..> ", TypeRelationType.OPTIONAL, hostingAssetType); } } -- 2.39.5 From 672d4ce0f14d996e45d6e01a3d79cc79c4c7a448 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jul 2024 06:36:04 +0200 Subject: [PATCH 3/9] generate separate PlantUML diagrams for meta-groups --- doc/hs-hosting-asset-type-structure.md | 144 ++++++++++++++---- .../hs/hosting/asset/HsHostingAssetType.java | 54 ++++--- ...HsDomainDnsSetupHostingAssetValidator.java | 1 - 3 files changed, 151 insertions(+), 48 deletions(-) diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md index ddd86373..7b5a185f 100644 --- a/doc/hs-hosting-asset-type-structure.md +++ b/doc/hs-hosting-asset-type-structure.md @@ -1,8 +1,9 @@ -### HostingAsset Type Structure +## HostingAsset Type Structure + +### packages Webspace, Domain, Server ```plantuml @startuml - left to right direction package Booking #99bcdb { @@ -15,22 +16,10 @@ package Booking #99bcdb { } package Hosting #white { - package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER - entity HA_IP_NUMBER - } - - package PostgreSQL #99bcdb { - entity HA_PGSQL_INSTANCE - entity HA_PGSQL_USER - entity HA_PGSQL_DATABASE - } - - package MariaDB #99bcdb { - entity HA_MARIADB_INSTANCE - entity HA_MARIADB_USER - entity HA_MARIADB_DATABASE + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS } package Domain #99bcdb { @@ -41,10 +30,10 @@ package Hosting #white { entity HA_EMAIL_ADDRESS } - package Webspace #99bcdb { - entity HA_MANAGED_WEBSPACE - entity HA_UNIX_USER - entity HA_EMAIL_ALIAS + package Server #99bcdb { + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER + entity HA_IP_NUMBER } } @@ -66,11 +55,56 @@ HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP -HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER -HA_PGSQL_USER *==> HA_PGSQL_INSTANCE -HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE -HA_PGSQL_DATABASE *==> HA_MANAGED_WEBSPACE -HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE +HA_IP_NUMBER o..> HA_CLOUD_SERVER +HA_IP_NUMBER o..> HA_MANAGED_SERVER +HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE +``` +### packages Webspace, MariaDB, Server + +```plantuml +@startuml +left to right direction + +package Booking #99bcdb { + entity BI_PRIVATE_CLOUD + entity BI_CLOUD_SERVER + entity BI_MANAGED_SERVER + entity BI_MANAGED_WEBSPACE + entity BI_DOMAIN_DNS_SETUP + entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP +} + +package Hosting #white { + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS + } + + package MariaDB #99bcdb { + 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_IP_NUMBER + } + +} + +BI_CLOUD_SERVER o..> BI_PRIVATE_CLOUD +BI_MANAGED_SERVER o..> BI_PRIVATE_CLOUD +BI_MANAGED_WEBSPACE o..> BI_MANAGED_SERVER + +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 @@ -80,5 +114,59 @@ HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE ``` +### packages PostgreSQL, Webspace, Server -This code generated was by HsHostingAssetType.main, do not amend manually. +```plantuml +@startuml +left to right direction + +package Booking #99bcdb { + entity BI_PRIVATE_CLOUD + entity BI_CLOUD_SERVER + entity BI_MANAGED_SERVER + entity BI_MANAGED_WEBSPACE + entity BI_DOMAIN_DNS_SETUP + entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP +} + +package Hosting #white { + package PostgreSQL #99bcdb { + entity HA_PGSQL_INSTANCE + entity HA_PGSQL_USER + entity HA_PGSQL_DATABASE + } + + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS + } + + package Server #99bcdb { + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER + entity HA_IP_NUMBER + } + +} + +BI_CLOUD_SERVER o..> BI_PRIVATE_CLOUD +BI_MANAGED_SERVER o..> BI_PRIVATE_CLOUD +BI_MANAGED_WEBSPACE o..> BI_MANAGED_SERVER + +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_PGSQL_INSTANCE *==> HA_MANAGED_SERVER +HA_PGSQL_USER *==> HA_PGSQL_INSTANCE +HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE +HA_PGSQL_DATABASE *==> HA_MANAGED_WEBSPACE +HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE +HA_IP_NUMBER o..> HA_CLOUD_SERVER +HA_IP_NUMBER o..> HA_MANAGED_SERVER +HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE +``` + #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 243f435b..2f7e2f66 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 @@ -9,11 +9,10 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toSet; import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; public enum HsHostingAssetType implements Node { @@ -137,13 +136,7 @@ public enum HsHostingAssetType implements Node { return type == null ? null : type.name(); } - public static void main(final String[] args) throws IOException { - - final var includedHostingGroups = stream(HsHostingAssetType.values()) - .map(t -> t.groupName) - .collect(toSet()); -// final var includedHostingGroups = Set.of("Domain", "Server", "Webspace"); - + private static void renderAsPlantUML(final Set includedHostingGroups) throws IOException { final String bookingNodes = stream(HsBookingItemType.values()) .map(t -> " entity " + t.nodeName()) .collect(joining("\n")); @@ -166,7 +159,7 @@ public enum HsHostingAssetType implements Node { Files.writeString( Path.of("doc/hs-hosting-asset-type-structure.md"), """ - ### HostingAsset Type Structure + ### packages %{packages} ```plantuml @startuml @@ -180,32 +173,55 @@ public enum HsHostingAssetType implements Node { %{hostingGroups} } - Booking -left-> Hosting - %{bookingItemEdges} %{hostingAssetEdges} ``` - - This code generated was by %{this}.main, do not amend manually. """ - .replace("%{this}", HsHostingAssetType.class.getSimpleName()) + .replace("%{packages}", String.join(", ", includedHostingGroups)) .replace("%{bookingNodes}", bookingNodes) .replace("%{hostingGroups}", hostingGroups) .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) .replace("%{bookingItemEdges}", bookingItemEdges) .replace("%{hostingAssetEdges}", hostingAssetEdges), - StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + StandardOpenOption.APPEND); } private static String generateGroup(final String group) { return " package " + group + " #99bcdb {\n" + stream(HsHostingAssetType.values()) - .filter(t -> t.groupName.equals(group)) - .map(t -> " entity " + t.nodeName()) - .collect(joining(" \n")) + .filter(t -> t.groupName.equals(group)) + .map(t -> " entity " + t.nodeName()) + .collect(joining(" \n")) + "\n }\n"; } + + public static void main(final String[] args) throws IOException { + + Files.writeString( + Path.of("doc/hs-hosting-asset-type-structure.md"), + """ + ## HostingAsset Type Structure + + """, + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + +// renderAsPlantUML(stream(HsHostingAssetType.values()) +// .map(t -> t.groupName) +// .collect(toSet())); + + renderAsPlantUML(Set.of("Domain", "Server", "Webspace")); + renderAsPlantUML(Set.of("Server", "Webspace", "MariaDB")); + renderAsPlantUML(Set.of("Server", "Webspace", "PostgreSQL")); + + Files.writeString( + Path.of("doc/hs-hosting-asset-type-structure.md"), + """ + #This code generated was by %{this}.main, do not amend manually. + """ + .replace("%{this}", HsHostingAssetType.class.getSimpleName()), + StandardOpenOption.APPEND); + } } enum TypeRelationType { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index 3205a0da..130e6255 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import lombok.SneakyThrows; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.system.SystemProcess; import java.util.List; -- 2.39.5 From ba3667079d5484d66df75597f40402986fd58610 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jul 2024 10:41:30 +0200 Subject: [PATCH 4/9] WIP: entity reference based on type-structure --- doc/hs-hosting-asset-type-structure.md | 108 ++++++++++------- .../hs/booking/item/HsBookingItemType.java | 2 +- .../hs/hosting/asset/HsHostingAssetType.java | 114 +++++++++++++----- .../HsCloudServerHostingAssetValidator.java | 6 +- ...HsDomainDnsSetupHostingAssetValidator.java | 6 +- .../HsDomainSetupHostingAssetValidator.java | 6 +- .../HsEMailAliasHostingAssetValidator.java | 4 +- .../HsHostingAssetEntityValidator.java | 98 ++++++--------- .../HsManagedServerHostingAssetValidator.java | 6 +- ...sManagedWebspaceHostingAssetValidator.java | 9 +- .../HsUnixUserHostingAssetValidator.java | 5 +- 11 files changed, 202 insertions(+), 162 deletions(-) diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md index 7b5a185f..24ebda74 100644 --- a/doc/hs-hosting-asset-type-structure.md +++ b/doc/hs-hosting-asset-type-structure.md @@ -1,6 +1,8 @@ + + ## HostingAsset Type Structure -### packages Webspace, Domain, Server +### packages Server, Webspace, Domain ```plantuml @startuml @@ -16,6 +18,12 @@ package Booking #99bcdb { } package Hosting #white { + 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 @@ -23,6 +31,7 @@ package Hosting #white { } package Domain #99bcdb { + entity HA_DOMAIN_SETUP entity HA_DOMAIN_DNS_SETUP entity HA_DOMAIN_HTTP_SETUP entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP @@ -30,27 +39,21 @@ package Hosting #white { entity HA_EMAIL_ADDRESS } - package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER - entity HA_IP_NUMBER - } - } -BI_CLOUD_SERVER o..> BI_PRIVATE_CLOUD -BI_MANAGED_SERVER o..> BI_PRIVATE_CLOUD -BI_MANAGED_WEBSPACE o..> BI_MANAGED_SERVER +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_DOMAIN_DNS_SETUP *==> BI_DOMAIN_DNS_SETUP +HA_DOMAIN_DNS_SETUP ==* BI_DOMAIN_DNS_SETUP HA_DOMAIN_HTTP_SETUP *==> HA_MANAGED_WEBSPACE HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER -HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> BI_DOMAIN_EMAIL_SUBMISSION_SETUP +HA_DOMAIN_EMAIL_SUBMISSION_SETUP ==* BI_DOMAIN_EMAIL_SUBMISSION_SETUP HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE @@ -58,8 +61,15 @@ HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE +package Legend { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 +} +Booking -down[hidden]->Legend ``` -### packages Webspace, MariaDB, Server +### packages MariaDB, Server, Webspace ```plantuml @startuml @@ -75,12 +85,6 @@ package Booking #99bcdb { } package Hosting #white { - package Webspace #99bcdb { - entity HA_MANAGED_WEBSPACE - entity HA_UNIX_USER - entity HA_EMAIL_ALIAS - } - package MariaDB #99bcdb { entity HA_MARIADB_INSTANCE entity HA_MARIADB_USER @@ -93,15 +97,21 @@ package Hosting #white { entity HA_IP_NUMBER } + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS + } + } -BI_CLOUD_SERVER o..> BI_PRIVATE_CLOUD -BI_MANAGED_SERVER o..> BI_PRIVATE_CLOUD -BI_MANAGED_WEBSPACE o..> BI_MANAGED_SERVER +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 @@ -113,8 +123,15 @@ 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 +package Legend { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 +} +Booking -down[hidden]->Legend ``` -### packages PostgreSQL, Webspace, Server +### packages Server, PostgreSQL, Webspace ```plantuml @startuml @@ -130,6 +147,12 @@ package Booking #99bcdb { } package Hosting #white { + package Server #99bcdb { + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER + entity HA_IP_NUMBER + } + package PostgreSQL #99bcdb { entity HA_PGSQL_INSTANCE entity HA_PGSQL_USER @@ -142,21 +165,15 @@ package Hosting #white { entity HA_EMAIL_ALIAS } - package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER - entity HA_IP_NUMBER - } - } -BI_CLOUD_SERVER o..> BI_PRIVATE_CLOUD -BI_MANAGED_SERVER o..> BI_PRIVATE_CLOUD -BI_MANAGED_WEBSPACE o..> BI_MANAGED_SERVER +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 @@ -168,5 +185,12 @@ HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE +package Legend { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 +} +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/booking/item/HsBookingItemType.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java index dfc0129e..720b3ecc 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java @@ -25,7 +25,7 @@ public enum HsBookingItemType implements Node { @Override public List edges() { return ofNullable(parentItemType) - .map(p -> (nodeName() + " o..> " + p.nodeName())) + .map(p -> (nodeName() + " *--> " + p.nodeName())) .stream().toList(); } 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 2f7e2f66..99f673fd 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 @@ -1,5 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset; +import lombok.AllArgsConstructor; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.Node; @@ -10,10 +12,13 @@ import java.nio.file.StandardOpenOption; import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.function.Function; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; public enum HsHostingAssetType implements Node { CLOUD_SERVER( // named e.g. vm1234 @@ -33,9 +38,10 @@ public enum HsHostingAssetType implements Node { inGroup("Webspace"), requiredParent(MANAGED_WEBSPACE)), -// DOMAIN_SETUP( // named e.g. example.org -// inGroup("Domain") -// ), + @Deprecated + DOMAIN_SETUP( // named e.g. example.org + inGroup("Domain") + ), DOMAIN_DNS_SETUP( // named e.g. example.org inGroup("Domain"), @@ -101,11 +107,11 @@ public enum HsHostingAssetType implements Node { ); final String groupName; - private final EntityTypeRelation[] relations; + private final EntityTypeRelation[] relations; HsHostingAssetType( final String groupName, - final EntityTypeRelation... relations + final EntityTypeRelation... relations ) { this.groupName = groupName; this.relations = relations; @@ -116,6 +122,45 @@ public enum HsHostingAssetType implements Node { return groupName; } + + public RelationPolicy bookingItemPolicy() { + return stream(relations).filter(r -> r.relatedType == (Object) HsBookingItemType.class).findAny() + .map(r -> r.relationType) + .orElse(RelationPolicy.FORBIDDEN); + } + + public HsBookingItemType bookingItemType() { + return stream(relations).filter(r -> r.relatedType == (Object) HsBookingItemType.class).findAny() + .map(r -> HsBookingItemType.valueOf(r.relatedType.toString())) + .orElse(null); + } + + public RelationPolicy parentAssetPolicy() { + return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + .map(r -> r.relationType) + .orElse(RelationPolicy.FORBIDDEN); + } + + public HsHostingAssetType parentAssetType() { + return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) + .orElse(null); + } + + public RelationPolicy assignedToAssetPolicy() { + return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + .map(r -> r.relationType) + .orElse(RelationPolicy.FORBIDDEN); + } + + public HsHostingAssetType assignedToAssetType() { + return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) + .orElse(null); + } + + + @Override public List edges() { return stream(relations) @@ -176,6 +221,13 @@ public enum HsHostingAssetType implements Node { %{bookingItemEdges} %{hostingAssetEdges} + package Legend { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 + } + Booking -down[hidden]->Legend ``` """ .replace("%{packages}", String.join(", ", includedHostingGroups)) @@ -201,6 +253,8 @@ public enum HsHostingAssetType implements Node { Files.writeString( Path.of("doc/hs-hosting-asset-type-structure.md"), """ + + ## HostingAsset Type Structure """, @@ -210,52 +264,48 @@ public enum HsHostingAssetType implements Node { // .map(t -> t.groupName) // .collect(toSet())); - renderAsPlantUML(Set.of("Domain", "Server", "Webspace")); - renderAsPlantUML(Set.of("Server", "Webspace", "MariaDB")); - renderAsPlantUML(Set.of("Server", "Webspace", "PostgreSQL")); + renderAsPlantUML(Set.of("Domain", "Webspace", "Server")); + renderAsPlantUML(Set.of("MariaDB", "Webspace", "Server")); + renderAsPlantUML(Set.of("PostgreSQL", "Webspace", "Server")); Files.writeString( Path.of("doc/hs-hosting-asset-type-structure.md"), """ - #This code generated was by %{this}.main, do not amend manually. + This code generated was by %{this}.main, do not amend manually. """ .replace("%{this}", HsHostingAssetType.class.getSimpleName()), StandardOpenOption.APPEND); } + + public enum RelationPolicy { + FORBIDDEN, OPTIONAL, REQUIRED + } } -enum TypeRelationType { - OPTIONAL, REQUIRED -} - -class EntityTypeRelation { - final String edge; - final TypeRelationType relationType; +@AllArgsConstructor +class EntityTypeRelation { + final HsHostingAssetType.RelationPolicy relationType; + private final Function getter; final T relatedType; + final String edge; - EntityTypeRelation(final String edge, final TypeRelationType required, final T relatedType) { - this.edge = edge; - this.relationType = required; - this.relatedType = relatedType; + static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { + return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getBookingItem, bookingItemType, " ==* "); } - static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { - return new EntityTypeRelation<>(" *==> ", TypeRelationType.REQUIRED, bookingItemType); + static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(OPTIONAL, null, hostingAssetType, " o..> "); } - static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" o..> ", TypeRelationType.OPTIONAL, hostingAssetType); + static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(REQUIRED, null, hostingAssetType, " *==> "); } - static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" *==> ", TypeRelationType.REQUIRED, hostingAssetType); + static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(REQUIRED, null, hostingAssetType, " o..> "); } - static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" o..> ", TypeRelationType.REQUIRED, hostingAssetType); - } - - static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(" o..> ", TypeRelationType.OPTIONAL, hostingAssetType); + static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(OPTIONAL, null, hostingAssetType, " o..> "); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java index 9144189b..55a54b42 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import java.util.regex.Pattern; @@ -9,9 +9,7 @@ class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator { HsCloudServerHostingAssetValidator() { super( - BookingItem.mustBeOfType(HsBookingItemType.CLOUD_SERVER), - ParentAsset.mustBeNull(), - AssignedToAsset.mustBeNull(), + HsHostingAssetType.CLOUD_SERVER, AlarmContact.isOptional(), NO_EXTRA_PROPERTIES); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index 130e6255..c314f1f6 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -9,6 +9,7 @@ import java.util.regex.Pattern; import static java.util.Arrays.stream; import static java.util.Optional.ofNullable; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP; import static net.hostsharing.hsadminng.hs.validation.ArrayProperty.arrayOf; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; @@ -31,9 +32,8 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT; HsDomainDnsSetupHostingAssetValidator() { - super( BookingItem.mustBeNull(), - ParentAsset.mustBeNull(), - AssignedToAsset.mustBeNull(), + super( + DOMAIN_DNS_SETUP, AlarmContact.isOptional(), integerProperty("TTL").min(0).withDefault(21600), diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java index d2693f7e..83300d5c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java @@ -4,6 +4,8 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import java.util.regex.Pattern; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; + class HsDomainSetupHostingAssetValidator extends HsHostingAssetEntityValidator { public static final String DOMAIN_NAME_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(? { static final ValidatableProperty[] NO_EXTRA_PROPERTIES = new ValidatableProperty[0]; - private final HsHostingAssetEntityValidator.BookingItem bookingItemValidation; - private final HsHostingAssetEntityValidator.ParentAsset parentAssetValidation; - private final HsHostingAssetEntityValidator.AssignedToAsset assignedToAssetValidation; + private final ReferenceValidator bookingItemReferenceValidation; + private final ReferenceValidator parentAssetReferenceValidation; + private final ReferenceValidator assignedToAssetReferenceValidation; private final HsHostingAssetEntityValidator.AlarmContact alarmContactValidation; HsHostingAssetEntityValidator( - @NotNull final BookingItem bookingItemValidation, - @NotNull final ParentAsset parentAssetValidation, - @NotNull final AssignedToAsset assignedToAssetValidation, - @NotNull final AlarmContact alarmContactValidation, - final ValidatableProperty... properties) { + final HsHostingAssetType assetType, final AlarmContact alarmContactValidation, final ValidatableProperty... properties) { super(properties); - this.bookingItemValidation = bookingItemValidation; - this.parentAssetValidation = parentAssetValidation; - this.assignedToAssetValidation = assignedToAssetValidation; + this.bookingItemReferenceValidation = new ReferenceValidator<>( + assetType.bookingItemPolicy(), + assetType.bookingItemType(), + HsHostingAssetEntity::getBookingItem, + HsBookingItemEntity::getType); + this.parentAssetReferenceValidation = new ReferenceValidator<>( + assetType.parentAssetPolicy(), + assetType.parentAssetType(), + HsHostingAssetEntity::getParentAsset, + HsHostingAssetEntity::getType); + this.assignedToAssetReferenceValidation = new ReferenceValidator<>( + assetType.assignedToAssetPolicy(), + assetType.assignedToAssetType(), + HsHostingAssetEntity::getAssignedToAsset, + HsHostingAssetEntity::getType); this.alarmContactValidation = alarmContactValidation; } @@ -63,9 +72,9 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator validateEntityReferencesAndProperties(final HsHostingAssetEntity assetEntity) { return Stream.of( - validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate), - validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate), - validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetValidation::validate), + validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate), + validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate), + validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetReferenceValidation::validate), validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate), validateProperties(assetEntity)) .filter(Objects::nonNull) @@ -137,15 +146,15 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { + static class ReferenceValidator { - private final Policy policy; + private final HsHostingAssetType.RelationPolicy policy; private final T subEntityType; private final Function subEntityGetter; private final Function subEntityTypeGetter; public ReferenceValidator( - final Policy policy, + final HsHostingAssetType.RelationPolicy policy, final T subEntityType, final Function subEntityGetter, final Function subEntityTypeGetter) { @@ -156,7 +165,7 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator subEntityGetter) { this.policy = policy; this.subEntityType = null; @@ -164,17 +173,13 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator null; } - enum Policy { - OPTIONAL, FORBIDDEN, REQUIRED - } - List validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) { final var subEntity = subEntityGetter.apply(assetEntity); - if (policy == Policy.REQUIRED && subEntity == null) { + if (policy == REQUIRED && subEntity == null) { return List.of(referenceFieldName + "' must not be null but is null"); } - if (policy == Policy.FORBIDDEN && subEntity != null) { + if ((policy == FORBIDDEN) && (subEntity != null)) { return List.of(referenceFieldName + "' must be null but is set to "+ assetEntity.getBookingItem().toShortString()); } final var subItemType = subEntity != null ? subEntityTypeGetter.apply(subEntity) : null; @@ -185,59 +190,28 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { + static class ParentAssetReferenceValidation extends ReferenceValidator { - BookingItem(final Policy policy, final HsBookingItemType bookingItemType) { - super(policy, bookingItemType, HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType); - } - - static BookingItem mustBeNull() { - return new BookingItem(Policy.FORBIDDEN, null); - } - - static BookingItem mustBeOfType(final HsBookingItemType hsBookingItemType) { - return new BookingItem(Policy.REQUIRED, hsBookingItemType); - } - } - - static class ParentAsset extends ReferenceValidator { - - ParentAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType parentAssetType) { + ParentAssetReferenceValidation(final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType parentAssetType) { super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType); } - - static ParentAsset mustBeNull() { - return new ParentAsset(Policy.FORBIDDEN, null); - } - - static ParentAsset mustBeOfType(final HsHostingAssetType hostingAssetType) { - return new ParentAsset(Policy.REQUIRED, hostingAssetType); - } - - static ParentAsset mustBeNullOrOfType(final HsHostingAssetType hostingAssetType) { - return new ParentAsset(Policy.OPTIONAL, hostingAssetType); - } } - static class AssignedToAsset extends ReferenceValidator { + static class AssignedToAssetReferenceValidation extends ReferenceValidator { - AssignedToAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType assignedToAssetType) { + AssignedToAssetReferenceValidation(final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType assignedToAssetType) { super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType); } - - static AssignedToAsset mustBeNull() { - return new AssignedToAsset(Policy.FORBIDDEN, null); - } } static class AlarmContact extends ReferenceValidator> { - AlarmContact(final ReferenceValidator.Policy policy) { + AlarmContact(final HsHostingAssetType.RelationPolicy policy) { super(policy, HsHostingAssetEntity::getAlarmContact); } static AlarmContact isOptional() { - return new AlarmContact(Policy.OPTIONAL); + return new AlarmContact(HsHostingAssetType.RelationPolicy.OPTIONAL); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java index 362abf38..69c0efff 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java @@ -1,10 +1,10 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import java.util.regex.Pattern; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; @@ -13,9 +13,7 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator public HsManagedServerHostingAssetValidator() { super( - BookingItem.mustBeOfType(HsBookingItemType.MANAGED_SERVER), - ParentAsset.mustBeNull(), // until we introduce a hosting asset for 'HOST' - AssignedToAsset.mustBeNull(), + MANAGED_SERVER, AlarmContact.isOptional(), // hostmaster alert address is implicitly added // monitoring 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 bffedf2f..857ec7b7 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,16 +1,15 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import java.util.regex.Pattern; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; + class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator { public HsManagedWebspaceHostingAssetValidator() { - super(BookingItem.mustBeOfType(HsBookingItemType.MANAGED_WEBSPACE), - ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_SERVER), // the (shared or private) ManagedServer - AssignedToAsset.mustBeNull(), + super( + MANAGED_SERVER, AlarmContact.isOptional(), // hostmaster alert address is implicitly added NO_EXTRA_PROPERTIES); } 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 309404f6..14096dbc 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 @@ -17,9 +17,8 @@ class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator { private static final int DASH_LENGTH = "-".length(); HsUnixUserHostingAssetValidator() { - super( BookingItem.mustBeNull(), - ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE), - AssignedToAsset.mustBeNull(), + super( + HsHostingAssetType.DOMAIN_DNS_SETUP, AlarmContact.isOptional(), integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(), -- 2.39.5 From f5802ed404f9e2e157a764ce262108fe45817728 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jul 2024 16:59:30 +0200 Subject: [PATCH 5/9] tests passing --- doc/hs-hosting-asset-type-structure.md | 69 +++--- .../hosting/asset/HsHostingAssetEntity.java | 3 + .../hs/hosting/asset/HsHostingAssetType.java | 195 +++++++++------- .../HsCloudServerHostingAssetValidator.java | 5 +- .../HsDomainSetupHostingAssetValidator.java | 31 +++ .../HsHostingAssetEntityValidator.java | 14 -- ...HsHostingAssetEntityValidatorRegistry.java | 1 + ...sManagedWebspaceHostingAssetValidator.java | 4 +- .../HsUnixUserHostingAssetValidator.java | 2 +- .../hs/validation/HsEntityValidator.java | 3 +- ...HostingAssetRepositoryIntegrationTest.java | 5 +- .../asset/HsHostingAssetTypeUnitTest.java | 219 ++++++++++++++++++ ...DnsSetupHostingAssetValidatorUnitTest.java | 2 + ...gAssetEntityValidatorRegistryUnitTest.java | 1 + 14 files changed, 414 insertions(+), 140 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md index 24ebda74..b03b7ced 100644 --- a/doc/hs-hosting-asset-type-structure.md +++ b/doc/hs-hosting-asset-type-structure.md @@ -1,14 +1,12 @@ - - ## HostingAsset Type Structure -### packages Server, Webspace, Domain +### Domain ```plantuml @startuml left to right direction -package Booking #99bcdb { +package Booking #feb28c { entity BI_PRIVATE_CLOUD entity BI_CLOUD_SERVER entity BI_MANAGED_SERVER @@ -17,7 +15,16 @@ package Booking #99bcdb { entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP } -package Hosting #white { +package Hosting #feb28c{ + package Domain #99bcdb { + entity HA_DOMAIN_SETUP + entity HA_DOMAIN_DNS_SETUP + entity HA_DOMAIN_HTTP_SETUP + entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP + entity HA_DOMAIN_EMAIL_MAILBOX_SETUP + entity HA_EMAIL_ADDRESS + } + package Server #99bcdb { entity HA_CLOUD_SERVER entity HA_MANAGED_SERVER @@ -30,15 +37,6 @@ package Hosting #white { entity HA_EMAIL_ALIAS } - package Domain #99bcdb { - entity HA_DOMAIN_SETUP - entity HA_DOMAIN_DNS_SETUP - entity HA_DOMAIN_HTTP_SETUP - entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP - entity HA_DOMAIN_EMAIL_MAILBOX_SETUP - entity HA_EMAIL_ADDRESS - } - } BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD @@ -50,18 +48,21 @@ 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_DOMAIN_DNS_SETUP ==* BI_DOMAIN_DNS_SETUP -HA_DOMAIN_HTTP_SETUP *==> HA_MANAGED_WEBSPACE +HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP +HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP +HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER -HA_DOMAIN_EMAIL_SUBMISSION_SETUP ==* BI_DOMAIN_EMAIL_SUBMISSION_SETUP +HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> HA_DOMAIN_SETUP HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE -HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_MANAGED_WEBSPACE +HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_DOMAIN_SETUP +HA_DOMAIN_EMAIL_MAILBOX_SETUP o..> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE -package Legend { + +package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 @@ -69,13 +70,13 @@ package Legend { } Booking -down[hidden]->Legend ``` -### packages MariaDB, Server, Webspace +### MariaDB ```plantuml @startuml left to right direction -package Booking #99bcdb { +package Booking #feb28c { entity BI_PRIVATE_CLOUD entity BI_CLOUD_SERVER entity BI_MANAGED_SERVER @@ -84,7 +85,7 @@ package Booking #99bcdb { entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP } -package Hosting #white { +package Hosting #feb28c{ package MariaDB #99bcdb { entity HA_MARIADB_INSTANCE entity HA_MARIADB_USER @@ -123,7 +124,8 @@ 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 -package Legend { + +package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 @@ -131,13 +133,13 @@ package Legend { } Booking -down[hidden]->Legend ``` -### packages Server, PostgreSQL, Webspace +### PostgreSQL ```plantuml @startuml left to right direction -package Booking #99bcdb { +package Booking #feb28c { entity BI_PRIVATE_CLOUD entity BI_CLOUD_SERVER entity BI_MANAGED_SERVER @@ -146,19 +148,19 @@ package Booking #99bcdb { entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP } -package Hosting #white { - package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER - entity HA_IP_NUMBER - } - +package Hosting #feb28c{ package PostgreSQL #99bcdb { 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_IP_NUMBER + } + package Webspace #99bcdb { entity HA_MANAGED_WEBSPACE entity HA_UNIX_USER @@ -185,7 +187,8 @@ HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE -package Legend { + +package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index 80f9294c..b56b01d0 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java @@ -203,6 +203,9 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti .switchOnColumn("type", inCaseOf("DOMAIN_SETUP", then -> { + // grant(ADMIN).to(currentlyAssumedRole() // FIXME + // oder: + // via with.incomingSuperRole("bookingItem", ADMIN); (s.u.) then.toRole(GLOBAL, GUEST).grantPermission(INSERT); then.toRole(GLOBAL, ADMIN).grantPermission(SELECT); // TODO.spec: replace by a proper solution }) 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 99f673fd..2834612d 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 @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.Node; +import javax.naming.NamingException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -16,11 +17,14 @@ import java.util.function.Function; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toSet; import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; public enum HsHostingAssetType implements Node { + SAME_TYPE, // pseudo-type for recursive references + CLOUD_SERVER( // named e.g. vm1234 inGroup("Server"), requires(HsBookingItemType.CLOUD_SERVER)), @@ -38,28 +42,29 @@ public enum HsHostingAssetType implements Node { inGroup("Webspace"), requiredParent(MANAGED_WEBSPACE)), - @Deprecated DOMAIN_SETUP( // named e.g. example.org - inGroup("Domain") + inGroup("Domain"), + optionalParent(SAME_TYPE) ), DOMAIN_DNS_SETUP( // named e.g. example.org inGroup("Domain"), - requires(HsBookingItemType.DOMAIN_DNS_SETUP)), + requiredParent(DOMAIN_SETUP)), DOMAIN_HTTP_SETUP( // named e.g. example.org inGroup("Domain"), - requiredParent(MANAGED_WEBSPACE), + requiredParent(DOMAIN_SETUP), assignedTo(UNIX_USER)), DOMAIN_EMAIL_SUBMISSION_SETUP( // named e.g. example.org inGroup("Domain"), - requires(HsBookingItemType.DOMAIN_EMAIL_SUBMISSION_SETUP), - optionallyAssignedTo(MANAGED_WEBSPACE)), + requiredParent(DOMAIN_SETUP), + assignedTo(MANAGED_WEBSPACE)), DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org inGroup("Domain"), - requiredParent(MANAGED_WEBSPACE)), + requiredParent(DOMAIN_SETUP), + assignedTo(MANAGED_WEBSPACE)), // TODO.spec: SECURE_MX @@ -106,7 +111,7 @@ public enum HsHostingAssetType implements Node { assignedTo(MANAGED_WEBSPACE) ); - final String groupName; + private final String groupName; private final EntityTypeRelation[] relations; HsHostingAssetType( @@ -117,6 +122,11 @@ public enum HsHostingAssetType implements Node { this.relations = relations; } + HsHostingAssetType() { + this.groupName = null; + this.relations = null; + } + /// just syntactic sugar private static String inGroup(final String groupName) { return groupName; @@ -124,50 +134,62 @@ public enum HsHostingAssetType implements Node { public RelationPolicy bookingItemPolicy() { - return stream(relations).filter(r -> r.relatedType == (Object) HsBookingItemType.class).findAny() + return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType) .map(r -> r.relationType) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(RelationPolicy.FORBIDDEN); } public HsBookingItemType bookingItemType() { - return stream(relations).filter(r -> r.relatedType == (Object) HsBookingItemType.class).findAny() + return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType) .map(r -> HsBookingItemType.valueOf(r.relatedType.toString())) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(null); } public RelationPolicy parentAssetPolicy() { - return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + return stream(relations).filter(r -> r.relatedType instanceof HsHostingAssetType) .map(r -> r.relationType) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(RelationPolicy.FORBIDDEN); } public HsHostingAssetType parentAssetType() { - return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + return stream(relations).filter(r -> r.relatedType instanceof HsHostingAssetType) .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(null); } public RelationPolicy assignedToAssetPolicy() { - return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class) .map(r -> r.relationType) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(RelationPolicy.FORBIDDEN); } public HsHostingAssetType assignedToAssetType() { - return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() + return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class) .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(null); } - + private static X onlyASingleElementExpectedException(Object a, Object b) { + throw new IllegalStateException("Only a single element expected to match criteria."); + } @Override public List edges() { return stream(relations) - .map(r -> nodeName() + r.edge + r.relatedType.nodeName()) + .map(r -> nodeName() + r.edge + resolveNode(r.relatedType).nodeName()) .toList(); } + private Node resolveNode(final Node node) { + return node == SAME_TYPE ? this : node; + } + @Override public String nodeName() { return "HA_" + name(); @@ -181,100 +203,107 @@ public enum HsHostingAssetType implements Node { return type == null ? null : type.name(); } - private static void renderAsPlantUML(final Set includedHostingGroups) throws IOException { + private static String renderAsPlantUML(final String caption, final Set includedHostingGroups) { final String bookingNodes = stream(HsBookingItemType.values()) .map(t -> " entity " + t.nodeName()) .collect(joining("\n")); - final String hostingGroups = includedHostingGroups.stream() + final String hostingGroups = includedHostingGroups.stream().sorted() .map(HsHostingAssetType::generateGroup) .collect(joining("\n")); final String hostingAssetNodes = stream(HsHostingAssetType.values()) - .filter(a -> includedHostingGroups.contains(a.groupName)) - .map(a -> "entity "+a.nodeName()) + .filter(t -> t.isInGroups(includedHostingGroups)) + .map(t -> "entity " + t.nodeName()) .collect(joining("\n")); final String bookingItemEdges = stream(HsBookingItemType.values()) .map(HsBookingItemType::edges) .flatMap(Collection::stream) .collect(joining("\n")); final String hostingAssetEdges = stream(HsHostingAssetType.values()) - .filter(t -> includedHostingGroups.contains(t.groupName)) + .filter(t -> t.isInGroups(includedHostingGroups)) .map(HsHostingAssetType::edges) .flatMap(Collection::stream) .collect(joining("\n")); - Files.writeString( - Path.of("doc/hs-hosting-asset-type-structure.md"), + return """ + + ### %{caption} + + ```plantuml + @startuml + left to right direction + + package Booking #feb28c { + %{bookingNodes} + } + + package Hosting #feb28c{ + %{hostingGroups} + } + + %{bookingItemEdges} + + %{hostingAssetEdges} + + package Legend #white { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 + } + Booking -down[hidden]->Legend + ``` """ - ### packages %{packages} + .replace("%{caption}", caption) + .replace("%{bookingNodes}", bookingNodes) + .replace("%{hostingGroups}", hostingGroups) + .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) + .replace("%{bookingItemEdges}", bookingItemEdges) + .replace("%{hostingAssetEdges}", hostingAssetEdges); + } - ```plantuml - @startuml - left to right direction - - package Booking #99bcdb { - %{bookingNodes} - } - - package Hosting #white { - %{hostingGroups} - } - - %{bookingItemEdges} - - %{hostingAssetEdges} - package Legend { - SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY - SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY - ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 - ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 - } - Booking -down[hidden]->Legend - ``` - """ - .replace("%{packages}", String.join(", ", includedHostingGroups)) - .replace("%{bookingNodes}", bookingNodes) - .replace("%{hostingGroups}", hostingGroups) - .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) - .replace("%{bookingItemEdges}", bookingItemEdges) - .replace("%{hostingAssetEdges}", hostingAssetEdges), - StandardOpenOption.APPEND); + private boolean isInGroups(final Set assetGroups) { + return groupName != null && assetGroups.contains(groupName); } private static String generateGroup(final String group) { return " package " + group + " #99bcdb {\n" + stream(HsHostingAssetType.values()) - .filter(t -> t.groupName.equals(group)) + .filter(t -> group.equals(t.groupName)) .map(t -> " entity " + t.nodeName()) - .collect(joining(" \n")) + .collect(joining("\n")) + "\n }\n"; } - public static void main(final String[] args) throws IOException { - - Files.writeString( - Path.of("doc/hs-hosting-asset-type-structure.md"), - """ - + static String renderAsEmbeddedPlantUml() { + final var markdown = new StringBuilder(""" ## HostingAsset Type Structure - """, - StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + """); -// renderAsPlantUML(stream(HsHostingAssetType.values()) -// .map(t -> t.groupName) -// .collect(toSet())); + // rendering all types in a single diagram is currently ignored + renderAsPlantUML("Domain", stream(HsHostingAssetType.values()) + .filter(t -> t.groupName != null) + .map(t -> t.groupName) + .collect(toSet())); - renderAsPlantUML(Set.of("Domain", "Webspace", "Server")); - renderAsPlantUML(Set.of("MariaDB", "Webspace", "Server")); - renderAsPlantUML(Set.of("PostgreSQL", "Webspace", "Server")); + markdown.append(renderAsPlantUML("Domain", Set.of("Domain", "Webspace", "Server"))) + .append(renderAsPlantUML("MariaDB", Set.of("MariaDB", "Webspace", "Server"))) + .append(renderAsPlantUML("PostgreSQL", Set.of("PostgreSQL", "Webspace", "Server"))); + markdown.append(""" + + This code generated was by %{this}.main, do not amend manually. + """ + .replace("%{this}", HsHostingAssetType.class.getSimpleName())); + + return markdown.toString(); + } + + public static void main(final String[] args) throws IOException, NamingException { Files.writeString( Path.of("doc/hs-hosting-asset-type-structure.md"), - """ - This code generated was by %{this}.main, do not amend manually. - """ - .replace("%{this}", HsHostingAssetType.class.getSimpleName()), - StandardOpenOption.APPEND); + renderAsEmbeddedPlantUml(), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } public enum RelationPolicy { @@ -285,27 +314,23 @@ public enum HsHostingAssetType implements Node { @AllArgsConstructor class EntityTypeRelation { final HsHostingAssetType.RelationPolicy relationType; - private final Function getter; + final Function getter; final T relatedType; final String edge; static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { - return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getBookingItem, bookingItemType, " ==* "); + return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> "); } static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(OPTIONAL, null, hostingAssetType, " o..> "); + return new EntityTypeRelation<>(OPTIONAL, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> "); } static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(REQUIRED, null, hostingAssetType, " *==> "); + return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> "); } static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(REQUIRED, null, hostingAssetType, " o..> "); - } - - static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(OPTIONAL, null, hostingAssetType, " o..> "); + return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getAssignedToAsset, hostingAssetType, " o..> "); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java index 55a54b42..9413dcf2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidator.java @@ -1,15 +1,16 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import java.util.regex.Pattern; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; + class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator { HsCloudServerHostingAssetValidator() { super( - HsHostingAssetType.CLOUD_SERVER, + CLOUD_SERVER, AlarmContact.isOptional(), NO_EXTRA_PROPERTIES); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java index 83300d5c..e16b1356 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import java.util.List; import java.util.regex.Pattern; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; @@ -20,6 +21,36 @@ class HsDomainSetupHostingAssetValidator extends HsHostingAssetEntityValidator { this.identifierPattern = Pattern.compile(DOMAIN_NAME_REGEX); } + @Override + public List validateEntity(final HsHostingAssetEntity assetEntity) { + // TODO.impl: for newly created entities, check the permission of setting up a domain + // + // reject, if the domain is any of these: + // hostsharing.com|net|org|coop, // just to be on the safe side + // [^.}+, // top-level-domain + // co.uk, org.uk, gov.uk, ac.uk, sch.uk, + // com.au, net.au, org.au, edu.au, gov.au, asn.au, id.au, + // co.jp, ne.jp, or.jp, ac.jp, go.jp, + // com.cn, net.cn, org.cn, gov.cn, edu.cn, ac.cn, + // com.br, net.br, org.br, gov.br, edu.br, mil.br, art.br, + // co.in, net.in, org.in, gen.in, firm.in, ind.in, + // com.mx, net.mx, org.mx, gob.mx, edu.mx, + // gov.it, edu.it, + // co.nz, net.nz, org.nz, govt.nz, ac.nz, school.nz, geek.nz, kiwi.nz, + // co.kr, ne.kr, or.kr, go.kr, re.kr, pe.kr + // + // allow if + // - user has Admin/Agent-role for all its sub-domains and the direct parent-Domain which are set up at at Hostsharing + // - domain has DNS zone with TXT record approval + // - parent-domain has DNS zone with TXT record approval + // - dom + // + // TXT-Record check: + // new InitialDirContext().getAttributes("dns:_netblocks.google.com", new String[] { "TXT"}).get("TXT").getAll(); + + return super.validateEntity(assetEntity); + } + @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { return identifierPattern; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java index af5f16fa..3f4e93cd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java @@ -190,20 +190,6 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { - - ParentAssetReferenceValidation(final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType parentAssetType) { - super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType); - } - } - - static class AssignedToAssetReferenceValidation extends ReferenceValidator { - - AssignedToAssetReferenceValidation(final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType assignedToAssetType) { - super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType); - } - } - static class AlarmContact extends ReferenceValidator> { AlarmContact(final HsHostingAssetType.RelationPolicy policy) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java index 432dee9a..3ae14256 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistry.java @@ -20,6 +20,7 @@ public class HsHostingAssetEntityValidatorRegistry { register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator()); register(UNIX_USER, new HsUnixUserHostingAssetValidator()); register(EMAIL_ALIAS, new HsEMailAliasHostingAssetValidator()); + register(DOMAIN_SETUP, new HsDomainSetupHostingAssetValidator()); register(DOMAIN_DNS_SETUP, new HsDomainDnsSetupHostingAssetValidator()); } 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 857ec7b7..443aea02 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 @@ -4,12 +4,12 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import java.util.regex.Pattern; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator { public HsManagedWebspaceHostingAssetValidator() { super( - MANAGED_SERVER, + MANAGED_WEBSPACE, AlarmContact.isOptional(), // hostmaster alert address is implicitly added NO_EXTRA_PROPERTIES); } 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 14096dbc..579c3134 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 @@ -18,7 +18,7 @@ class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator { HsUnixUserHostingAssetValidator() { super( - HsHostingAssetType.DOMAIN_DNS_SETUP, + HsHostingAssetType.UNIX_USER, AlarmContact.isOptional(), integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(), 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 de4b70bc..fac624cf 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.validation; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -18,7 +19,7 @@ public abstract class HsEntityValidator { public final ValidatableProperty[] propertyValidators; - public HsEntityValidator(final ValidatableProperty... validators) { + public > HsEntityValidator(final ValidatableProperty... validators) { propertyValidators = validators; stream(propertyValidators).forEach(p -> p.deferredInit(propertyValidators)); } 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 c1849531..fa0855d6 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 @@ -27,6 +27,7 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf; @@ -165,8 +166,9 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu context("person-SmithPeter@example.com"); final var result = attempt(em, () -> { final var newAsset = HsHostingAssetEntity.builder() - .caption("some new domain setup") + .type(DOMAIN_SETUP) .identifier("example.org") + .caption("some new domain setup") .build(); return toCleanup(assetRepo.save(newAsset)); }); @@ -181,7 +183,6 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu } private void assertThatAssetIsPersisted(final HsHostingAssetEntity saved) { - final var context = attempt(em, () -> { final var found = assetRepo.findByUuid(saved.getUuid()); assertThat(found).isNotEmpty().map(HsHostingAssetEntity::toString).get().isEqualTo(saved.toString()); 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 new file mode 100644 index 00000000..794c3f25 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java @@ -0,0 +1,219 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class HsHostingAssetTypeUnitTest { + + @Test + void generatedPlantUML() { + final var result = HsHostingAssetType.renderAsEmbeddedPlantUml(); + + assertThat(result).isEqualTo(""" + ## HostingAsset Type Structure + + + ### Domain + + ```plantuml + @startuml + left to right direction + + package Booking #feb28c { + entity BI_PRIVATE_CLOUD + entity BI_CLOUD_SERVER + entity BI_MANAGED_SERVER + entity BI_MANAGED_WEBSPACE + entity BI_DOMAIN_DNS_SETUP + entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP + } + + package Hosting #feb28c{ + package Domain #99bcdb { + entity HA_DOMAIN_SETUP + entity HA_DOMAIN_DNS_SETUP + entity HA_DOMAIN_HTTP_SETUP + entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP + entity HA_DOMAIN_EMAIL_MAILBOX_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 + entity HA_EMAIL_ALIAS + } + + } + + 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_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER + HA_UNIX_USER *==> HA_MANAGED_WEBSPACE + HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP + HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP + HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP + HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER + HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> HA_DOMAIN_SETUP + HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_DOMAIN_SETUP + HA_DOMAIN_EMAIL_MAILBOX_SETUP o..> HA_MANAGED_WEBSPACE + HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE + HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP + HA_IP_NUMBER o..> HA_CLOUD_SERVER + HA_IP_NUMBER o..> HA_MANAGED_SERVER + HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE + + package Legend #white { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 + } + Booking -down[hidden]->Legend + ``` + + ### MariaDB + + ```plantuml + @startuml + left to right direction + + package Booking #feb28c { + entity BI_PRIVATE_CLOUD + entity BI_CLOUD_SERVER + entity BI_MANAGED_SERVER + entity BI_MANAGED_WEBSPACE + entity BI_DOMAIN_DNS_SETUP + entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP + } + + package Hosting #feb28c{ + package MariaDB #99bcdb { + 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_IP_NUMBER + } + + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS + } + + } + + 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_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_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 + + package Legend #white { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 + } + Booking -down[hidden]->Legend + ``` + + ### PostgreSQL + + ```plantuml + @startuml + left to right direction + + package Booking #feb28c { + entity BI_PRIVATE_CLOUD + entity BI_CLOUD_SERVER + entity BI_MANAGED_SERVER + entity BI_MANAGED_WEBSPACE + entity BI_DOMAIN_DNS_SETUP + entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP + } + + package Hosting #feb28c{ + package PostgreSQL #99bcdb { + 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_IP_NUMBER + } + + package Webspace #99bcdb { + entity HA_MANAGED_WEBSPACE + entity HA_UNIX_USER + entity HA_EMAIL_ALIAS + } + + } + + 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_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER + HA_UNIX_USER *==> HA_MANAGED_WEBSPACE + HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE + HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER + HA_PGSQL_USER *==> HA_PGSQL_INSTANCE + HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE + HA_PGSQL_DATABASE *==> HA_MANAGED_WEBSPACE + HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE + HA_IP_NUMBER o..> HA_CLOUD_SERVER + HA_IP_NUMBER o..> HA_MANAGED_SERVER + HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE + + package Legend #white { + SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY + SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY + ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 + ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2 + } + Booking -down[hidden]->Legend + ``` + + This code generated was by HsHostingAssetType.main, do not amend manually. + """); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index d1ea3563..671b9452 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -11,6 +11,7 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_COMMENT; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_DATA; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_TYPE; @@ -22,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat; class HsDomainDnsSetupHostingAssetValidatorUnitTest { static final HsHostingAssetEntity validDomainSetupEntity = HsHostingAssetEntity.builder() + .type(DOMAIN_SETUP) .identifier("example.org") .build(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistryUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistryUnitTest.java index ebe24e5e..4e4abae9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistryUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorRegistryUnitTest.java @@ -34,6 +34,7 @@ class HsHostingAssetEntityValidatorRegistryUnitTest { HsHostingAssetType.MANAGED_WEBSPACE, HsHostingAssetType.UNIX_USER, HsHostingAssetType.EMAIL_ALIAS, + HsHostingAssetType.DOMAIN_SETUP, HsHostingAssetType.DOMAIN_DNS_SETUP ); } -- 2.39.5 From 6f36cb215b307e294c451b256d649f8c0b379563 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jul 2024 18:37:19 +0200 Subject: [PATCH 6/9] improve RBAC grants --- .../hsadminng/hs/hosting/asset/HsHostingAssetEntity.java | 5 ++--- .../701-hosting-asset/7013-hs-hosting-asset-rbac.md | 3 +++ .../701-hosting-asset/7013-hs-hosting-asset-rbac.sql | 3 ++- .../asset/HsHostingAssetRepositoryIntegrationTest.java | 6 ++++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index b56b01d0..fd094a44 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java @@ -50,6 +50,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST; @@ -203,15 +204,13 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti .switchOnColumn("type", inCaseOf("DOMAIN_SETUP", then -> { - // grant(ADMIN).to(currentlyAssumedRole() // FIXME - // oder: - // via with.incomingSuperRole("bookingItem", ADMIN); (s.u.) then.toRole(GLOBAL, GUEST).grantPermission(INSERT); then.toRole(GLOBAL, ADMIN).grantPermission(SELECT); // TODO.spec: replace by a proper solution }) ) .createRole(OWNER, (with) -> { + with.owningUser(CREATOR); with.incomingSuperRole("bookingItem", ADMIN); with.incomingSuperRole("parentAsset", ADMIN); with.permission(DELETE); diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.md b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.md index 37b47e15..197b15f4 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.md +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.md @@ -80,6 +80,9 @@ subgraph parentAsset["`**parentAsset**`"] end end +%% granting roles to users +user:creator ==> role:asset:OWNER + %% granting roles to roles role:bookingItem:OWNER -.-> role:bookingItem:ADMIN role:bookingItem:ADMIN -.-> role:bookingItem:AGENT diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql index 5b740226..13fdcace 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql @@ -51,7 +51,8 @@ begin permissions => array['DELETE'], incomingSuperRoles => array[ hsBookingItemADMIN(newBookingItem), - hsHostingAssetADMIN(newParentAsset)] + hsHostingAssetADMIN(newParentAsset)], + userUuids => array[currentUserUuid()] ); perform createRoleWithGrants( 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 fa0855d6..8ad6e99d 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 @@ -134,6 +134,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu "{ grant perm:hs_hosting_asset#fir00:SELECT to role:global#global:ADMIN by system and assume }", // workaround // owner + "{ grant role:hs_hosting_asset#fir00:OWNER to user:superuser-alex@hostsharing.net by hs_hosting_asset#fir00:OWNER and assume }", "{ grant role:hs_hosting_asset#fir00:OWNER to role:hs_booking_item#fir01:ADMIN by system and assume }", "{ grant role:hs_hosting_asset#fir00:OWNER to role:hs_hosting_asset#vm1011:ADMIN by system and assume }", "{ grant perm:hs_hosting_asset#fir00:DELETE to role:hs_hosting_asset#fir00:OWNER by system and assume }", @@ -177,15 +178,16 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu result.assertSuccessful(); assertThat(result.returnedValue()).isNotNull().extracting(HsHostingAssetEntity::getUuid).isNotNull(); assertThat(result.returnedValue().isLoaded()).isFalse(); - context("superuser-alex@hostsharing.net"); + context("person-SmithPeter@example.com"); assertThatAssetIsPersisted(result.returnedValue()); + context("superuser-alex@hostsharing.net"); assertThat(assetRepo.count()).isEqualTo(assetCount + 1); } private void assertThatAssetIsPersisted(final HsHostingAssetEntity saved) { attempt(em, () -> { final var found = assetRepo.findByUuid(saved.getUuid()); - assertThat(found).isNotEmpty().map(HsHostingAssetEntity::toString).get().isEqualTo(saved.toString()); + assertThat(found).isNotEmpty().map(HsHostingAssetEntity::toString).contains(saved.toString()); }); } -- 2.39.5 From 3b7c35cfe4165ae50d1e3ea9ca06d23ad6980da6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 9 Jul 2024 08:33:13 +0200 Subject: [PATCH 7/9] add new packages to ArchitectureTest --- .../hsadminng/arch/ArchitectureTest.java | 16 ++++++++++++++++ ...ocessTest.java => SystemProcessUnitTest.java} | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) rename src/test/java/net/hostsharing/hsadminng/system/{SystemProcessTest.java => SystemProcessUnitTest.java} (98%) diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java index f626a3ed..cc2dafa6 100644 --- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java +++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java @@ -40,8 +40,10 @@ public class ArchitectureTest { "..test.pac", "..test.dom", "..context", + "..hash", "..generated..", "..persistence..", + "..system..", "..validation..", "..hs.office.bankaccount", "..hs.office.contact", @@ -110,6 +112,13 @@ public class ArchitectureTest { .should().onlyDependOnClassesThat() .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule hashPackageRule = classes() + .that().resideInAPackage("..hash..") + .should().onlyDependOnClassesThat() + .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + @ArchTest @SuppressWarnings("unused") public static final ArchRule errorsPackageRule = classes() @@ -117,6 +126,13 @@ public class ArchitectureTest { .should().onlyDependOnClassesThat() .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule systemPackageRule = classes() + .that().resideInAPackage("..system..") + .should().onlyDependOnClassesThat() + .resideOutsideOfPackage(NET_HOSTSHARING_HSADMINNG); + @ArchTest @SuppressWarnings("unused") public static final ArchRule testPackagesRule = classes() diff --git a/src/test/java/net/hostsharing/hsadminng/system/SystemProcessTest.java b/src/test/java/net/hostsharing/hsadminng/system/SystemProcessUnitTest.java similarity index 98% rename from src/test/java/net/hostsharing/hsadminng/system/SystemProcessTest.java rename to src/test/java/net/hostsharing/hsadminng/system/SystemProcessUnitTest.java index e1924adf..5025555c 100644 --- a/src/test/java/net/hostsharing/hsadminng/system/SystemProcessTest.java +++ b/src/test/java/net/hostsharing/hsadminng/system/SystemProcessUnitTest.java @@ -9,7 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.junit.jupiter.api.condition.OS.LINUX; -class SystemProcessTest { +class SystemProcessUnitTest { @Test @EnabledOnOs(LINUX) -- 2.39.5 From 6ddc0e08f7ea93fa4dc11f6eb3619e80b45c9851 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 9 Jul 2024 10:44:53 +0200 Subject: [PATCH 8/9] add |DNS to identifier of Domain DNS Setup entity --- .../hosting/asset/HsHostingAssetEntity.java | 2 +- .../hs/hosting/asset/HsHostingAssetType.java | 1 + ...HsDomainDnsSetupHostingAssetValidator.java | 13 ++++-- .../HsHostingAssetEntityValidator.java | 3 +- .../db/changelog/0-basis/010-context.sql | 2 +- .../7013-hs-hosting-asset-rbac.md | 4 +- .../7013-hs-hosting-asset-rbac.sql | 5 +-- .../7018-hs-hosting-asset-test-data.sql | 18 ++++---- ...sHostingAssetControllerAcceptanceTest.java | 41 +++++++++++++++++++ ...HostingAssetRepositoryIntegrationTest.java | 21 +++++----- ...DnsSetupHostingAssetValidatorUnitTest.java | 23 ++++++----- .../test/ContextBasedTestWithCleanup.java | 3 +- 12 files changed, 91 insertions(+), 45 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index fd094a44..55b8d00e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java @@ -205,12 +205,12 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti .switchOnColumn("type", inCaseOf("DOMAIN_SETUP", then -> { then.toRole(GLOBAL, GUEST).grantPermission(INSERT); - then.toRole(GLOBAL, ADMIN).grantPermission(SELECT); // TODO.spec: replace by a proper solution }) ) .createRole(OWNER, (with) -> { with.owningUser(CREATOR); + with.incomingSuperRole(GLOBAL, ADMIN).unassumed(); // TODO.spec: replace by a better solution with.incomingSuperRole("bookingItem", ADMIN); with.incomingSuperRole("parentAsset", ADMIN); with.permission(DELETE); 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 2834612d..32388aa0 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 @@ -132,6 +132,7 @@ public enum HsHostingAssetType implements Node { return groupName; } + // TODO.refa: try to get rid of the following similar methods: public RelationPolicy bookingItemPolicy() { return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index c314f1f6..eea095fb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -30,6 +30,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato static final String RR_REGEX_IN_TTL = RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT; + public static final String IDENTIFIER_SUFFIX = "|DNS"; HsDomainDnsSetupHostingAssetValidator() { super( @@ -59,14 +60,14 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { - return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + "$"); + return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + 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())); + ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); } } @@ -76,7 +77,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato final var result = super.validateContext(assetEntity); // TODO.spec: define which checks should get raised to error level - final var namedCheckZone = new SystemProcess("named-checkzone", assetEntity.getIdentifier()); + final var namedCheckZone = new SystemProcess("named-checkzone", fqdn(assetEntity)); if (namedCheckZone.execute(toZonefileString(assetEntity)) != 0) { // yes, named-checkzone writes error messages to stdout stream(namedCheckZone.getStdOut().split("\n")) @@ -98,8 +99,12 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato {userRRs} """ - .replace("{domain}", assetEntity.getIdentifier()) + .replace("{domain}", fqdn(assetEntity)) .replace("{ttl}", getPropertyValue(assetEntity, "TTL")) .replace("{userRRs}", getPropertyValues(assetEntity, "user-RR") ); } + + private String fqdn(final HsHostingAssetEntity assetEntity) { + return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length()-IDENTIFIER_SUFFIX.length()); + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java index 3f4e93cd..53858c29 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java @@ -21,6 +21,7 @@ import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.FORBIDDEN; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { @@ -183,7 +184,7 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator role:bookingItem:TENANT role:global:ADMIN -.-> role:alarmContact:OWNER role:alarmContact:OWNER -.-> role:alarmContact:ADMIN role:alarmContact:ADMIN -.-> role:alarmContact:REFERRER +role:global:ADMIN ==>|XX| role:asset:OWNER role:bookingItem:ADMIN ==> role:asset:OWNER role:parentAsset:ADMIN ==> role:asset:OWNER role:asset:OWNER ==> role:asset:ADMIN @@ -107,7 +108,6 @@ role:alarmContact:ADMIN ==> role:asset:TENANT role:global:ADMIN ==> perm:asset:INSERT role:parentAsset:ADMIN ==> perm:asset:INSERT role:global:GUEST ==> perm:asset:INSERT -role:global:ADMIN ==> perm:asset:SELECT role:asset:OWNER ==> perm:asset:DELETE role:asset:ADMIN ==> perm:asset:UPDATE role:asset:TENANT ==> perm:asset:SELECT diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql index 13fdcace..91afe2b6 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7013-hs-hosting-asset-rbac.sql @@ -50,6 +50,7 @@ begin hsHostingAssetOWNER(NEW), permissions => array['DELETE'], incomingSuperRoles => array[ + globalADMIN(unassumed()), hsBookingItemADMIN(newBookingItem), hsHostingAssetADMIN(newParentAsset)], userUuids => array[currentUserUuid()] @@ -86,10 +87,6 @@ begin IF NEW.type = 'DOMAIN_SETUP' THEN END IF; - - - call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), globalAdmin()); - call leaveTriggerForObjectUuid(NEW.uuid); 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 736c129d..26ef2ac8 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 @@ -71,15 +71,15 @@ begin defaultPrefix := relatedDebitor.defaultPrefix; insert into hs_hosting_asset - (uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config) - values (managedServerUuid, relatedManagedServerBookingItem.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(), relatedCloudServerBookingItem.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), - (managedWebspaceUuid, relatedManagedWebspaceBookingItem.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', 'some Domain-DNS-Setup', '{}'::jsonb), - (uuid_generate_v4(), null, 'DOMAIN_HTTP_SETUP', domainSetupUuid, webUnixUserUuid, defaultPrefix || '.example.org', 'some Domain-HTTP-Setup', '{ "option-htdocsfallback": true, "use-fcgiphpbin": "/usr/lib/cgi-bin/php", "validsubdomainnames": "*"}'::jsonb); + (uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config) + values (managedServerUuid, relatedManagedServerBookingItem.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(), relatedCloudServerBookingItem.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb), + (managedWebspaceUuid, relatedManagedWebspaceBookingItem.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); end; $$; --// 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 20ebd989..e45a157b 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 @@ -235,6 +235,47 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup assertThat(newUserUuid).isNotNull(); } + @Test + void globalAdmin_canAddTopLevelAsset() { + + context.define("superuser-alex@hostsharing.net"); + + final var location = RestAssured // @formatter:off + .given() + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(ContentType.JSON) + .body(""" + { + "type": "DOMAIN_SETUP", + "identifier": "example.com", + "caption": "some unrelated domain-setup", + "config": {} + } + """) + .port(port) + .when() + .post("http://localhost/api/hs/hosting/assets") + .then().log().all().assertThat() + .statusCode(201) + .contentType(ContentType.JSON) + .body("", lenientlyEquals(""" + { + "type": "DOMAIN_SETUP", + "identifier": "example.com", + "caption": "some unrelated domain-setup", + "config": {} + } + """)) + .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( + location.substring(location.lastIndexOf('/') + 1)); + assertThat(newWebspace).isNotNull(); + toCleanup(HsHostingAssetEntity.class, newWebspace); + } + @Test void propertyValidationsArePerformend_whenAddingAsset() { 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 8ad6e99d..40f38d7b 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 @@ -131,7 +131,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu initialGrantNames, // global-admin - "{ grant perm:hs_hosting_asset#fir00:SELECT to role:global#global:ADMIN by system and assume }", // workaround + "{ grant role:hs_hosting_asset#fir00:OWNER to role:global#global:ADMIN by system }", // workaround // owner "{ grant role:hs_hosting_asset#fir00:OWNER to user:superuser-alex@hostsharing.net by hs_hosting_asset#fir00:OWNER and assume }", @@ -159,37 +159,38 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu @Test public void anyUser_canCreateNewDomainSetupAsset() { - // given - context("superuser-alex@hostsharing.net"); - final var assetCount = assetRepo.count(); - // when context("person-SmithPeter@example.com"); final var result = attempt(em, () -> { final var newAsset = HsHostingAssetEntity.builder() .type(DOMAIN_SETUP) - .identifier("example.org") + .identifier("example.net") .caption("some new domain setup") .build(); - return toCleanup(assetRepo.save(newAsset)); + return assetRepo.save(newAsset); }); // then + // ... the domain setup was created and returned result.assertSuccessful(); assertThat(result.returnedValue()).isNotNull().extracting(HsHostingAssetEntity::getUuid).isNotNull(); assertThat(result.returnedValue().isLoaded()).isFalse(); + + // ... the creating user can read the new domain setup context("person-SmithPeter@example.com"); assertThatAssetIsPersisted(result.returnedValue()); - context("superuser-alex@hostsharing.net"); - assertThat(assetRepo.count()).isEqualTo(assetCount + 1); + + // ... a global admin can see the new domain setup as well if the domain OWNER role is assumed + context("superuser-alex@hostsharing.net", "hs_hosting_asset#example.net:OWNER"); // only works with the assumed role + assertThatAssetIsPersisted(result.returnedValue()); } private void assertThatAssetIsPersisted(final HsHostingAssetEntity saved) { + em.clear(); attempt(em, () -> { final var found = assetRepo.findByUuid(saved.getUuid()); assertThat(found).isNotEmpty().map(HsHostingAssetEntity::toString).contains(saved.toString()); }); - } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index 671b9452..6a4e1026 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -31,7 +31,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { return HsHostingAssetEntity.builder() .type(DOMAIN_DNS_SETUP) .parentAsset(validDomainSetupEntity) - .identifier("example.org") + .identifier("example.org|DNS") .config(Map.ofEntries( entry("user-RR", Array.of( "@ 1814400 IN XXX example.org. root.example.org ( 1234 10800 900 604800 86400 )", @@ -74,19 +74,20 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { void preprocessesTakesIdentifierFromParent() { // given final var givenEntity = validEntityBuilder().build(); + assertThat(givenEntity.getParentAsset().getIdentifier()).as("preconditon failed").isEqualTo("example.org"); final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); // when validator.preprocessEntity(givenEntity); // then - assertThat(givenEntity.getIdentifier()).isEqualTo(givenEntity.getParentAsset().getIdentifier()); + assertThat(givenEntity.getIdentifier()).isEqualTo("example.org|DNS"); } @Test void rejectsInvalidIdentifier() { // given - final var givenEntity = validEntityBuilder().identifier("wrong.org").build(); + final var givenEntity = validEntityBuilder().identifier("example.org").build(); final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); // when @@ -94,14 +95,14 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^example.org$', but is 'wrong.org'" + "'identifier' expected to match '^example.org|DNS$', but is 'example.org'" ); } @Test void acceptsValidIdentifier() { // given - final var givenEntity = validEntityBuilder().identifier(validDomainSetupEntity.getIdentifier()).build(); + final var givenEntity = validEntityBuilder().identifier(validDomainSetupEntity.getIdentifier()+"|DNS").build(); final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); // when @@ -126,9 +127,9 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'DOMAIN_DNS_SETUP:example.org.bookingItem' must be null but is set to D-???????-?:null", - "'DOMAIN_DNS_SETUP:example.org.parentAsset' must be of type DOMAIN_SETUP but is of type null", - "'DOMAIN_DNS_SETUP:example.org.assignedToAsset' must be null but is set to D-???????-?:null"); + "'DOMAIN_DNS_SETUP:example.org|DNS.bookingItem' must be null but is set to D-???????-?:null", + "'DOMAIN_DNS_SETUP:example.org|DNS.parentAsset' must be of type DOMAIN_SETUP but is of type null", + "'DOMAIN_DNS_SETUP:example.org|DNS.assignedToAsset' must be null but is set to D-???????-?:null"); } @Test @@ -162,9 +163,9 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'DOMAIN_DNS_SETUP:example.org.config.TTL' is expected to be of type class java.lang.Integer, but is of type 'String'", - "'DOMAIN_DNS_SETUP:example.org.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", - "'DOMAIN_DNS_SETUP:example.org.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); + "'DOMAIN_DNS_SETUP:example.org|DNS.config.TTL' is expected to be of type class java.lang.Integer, but is of type 'String'", + "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but '@ 1814400 IN 1814400 BAD1 TTL only allowed once' does not match any", + "'DOMAIN_DNS_SETUP:example.org|DNS.config.user-RR' is expected to match any of [([a-z0-9\\.-]+|@)\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*IN\\s+[A-Z]+\\s+[^;].*(;.*)*, ([a-z0-9\\.-]+|@)\\s+IN\\s+(([1-9][0-9]*[mMhHdDwW]{0,1})+\\s+)*[A-Z]+\\s+[^;].*(;.*)*] but 'www BAD1 Record-Class missing / not enough columns' does not match any"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/ContextBasedTestWithCleanup.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/ContextBasedTestWithCleanup.java index 5e9d8347..14e8619a 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/ContextBasedTestWithCleanup.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/ContextBasedTestWithCleanup.java @@ -8,7 +8,6 @@ import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; import org.springframework.beans.factory.annotation.Autowired; @@ -171,7 +170,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest { this.testInfo = testInfo; } - @AfterEach + //@AfterEach void cleanupAndCheckCleanup(final TestInfo testInfo) { // If the whole test method has its own transaction, cleanup makes no sense. // If that transaction even failed, cleaunup would cause an exception. -- 2.39.5 From 89cd2ec6e2b82bf595dd43d6a7246ad16a25e758 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 9 Jul 2024 14:27:55 +0200 Subject: [PATCH 9/9] cleanup after code-review --- .../hs/hosting/asset/HsHostingAssetType.java | 64 ++++++---- ...HsDomainDnsSetupHostingAssetValidator.java | 2 +- .../HsHostingAssetEntityValidator.java | 98 ++++++++------- .../HsHostingAssetControllerRestTest.java | 18 +++ ...udServerHostingAssetValidatorUnitTest.java | 14 +-- ...DnsSetupHostingAssetValidatorUnitTest.java | 14 +-- ...ainSetupHostingAssetValidatorUnitTest.java | 112 ++++++++++++++++++ ...ailAliasHostingAssetValidatorUnitTest.java | 4 +- ...edServerHostingAssetValidatorUnitTest.java | 19 +-- ...WebspaceHostingAssetValidatorUnitTest.java | 9 +- .../test/ContextBasedTestWithCleanup.java | 3 +- 11 files changed, 262 insertions(+), 95 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.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 32388aa0..6a0846a9 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 @@ -21,6 +21,9 @@ import static java.util.stream.Collectors.toSet; import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.ASSIGNED_TO_ASSET; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.BOOKING_ITEM; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.PARENT_ASSET; public enum HsHostingAssetType implements Node { SAME_TYPE, // pseudo-type for recursive references @@ -135,43 +138,49 @@ public enum HsHostingAssetType implements Node { // TODO.refa: try to get rid of the following similar methods: public RelationPolicy bookingItemPolicy() { - return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType) - .map(r -> r.relationType) + return stream(relations) + .filter(r -> r.relationType == BOOKING_ITEM) + .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(RelationPolicy.FORBIDDEN); } public HsBookingItemType bookingItemType() { - return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType) - .map(r -> HsBookingItemType.valueOf(r.relatedType.toString())) + return stream(relations) + .filter(r -> r.relationType == BOOKING_ITEM) + .map(r -> HsBookingItemType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(null); } public RelationPolicy parentAssetPolicy() { - return stream(relations).filter(r -> r.relatedType instanceof HsHostingAssetType) - .map(r -> r.relationType) + return stream(relations) + .filter(r -> r.relationType == PARENT_ASSET) + .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(RelationPolicy.FORBIDDEN); } public HsHostingAssetType parentAssetType() { - return stream(relations).filter(r -> r.relatedType instanceof HsHostingAssetType) - .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) + return stream(relations) + .filter(r -> r.relationType == PARENT_ASSET) + .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(null); } public RelationPolicy assignedToAssetPolicy() { - return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class) - .map(r -> r.relationType) + return stream(relations) + .filter(r -> r.relationType == ASSIGNED_TO_ASSET) + .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(RelationPolicy.FORBIDDEN); } public HsHostingAssetType assignedToAssetType() { - return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class) - .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) + return stream(relations) + .filter(r -> r.relationType == ASSIGNED_TO_ASSET) + .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) .orElse(null); } @@ -183,14 +192,10 @@ public enum HsHostingAssetType implements Node { @Override public List edges() { return stream(relations) - .map(r -> nodeName() + r.edge + resolveNode(r.relatedType).nodeName()) + .map(r -> nodeName() + r.edge + r.relatedType(this).nodeName()) .toList(); } - private Node resolveNode(final Node node) { - return node == SAME_TYPE ? this : node; - } - @Override public String nodeName() { return "HA_" + name(); @@ -310,28 +315,41 @@ public enum HsHostingAssetType implements Node { public enum RelationPolicy { FORBIDDEN, OPTIONAL, REQUIRED } + + public enum RelationType { + BOOKING_ITEM, + PARENT_ASSET, + ASSIGNED_TO_ASSET + } } @AllArgsConstructor class EntityTypeRelation { - final HsHostingAssetType.RelationPolicy relationType; + + final HsHostingAssetType.RelationPolicy relationPolicy; + final HsHostingAssetType.RelationType relationType; final Function getter; - final T relatedType; + private final T relatedType; final String edge; + public T relatedType(final HsHostingAssetType referringType) { + //noinspection unchecked + return relatedType == HsHostingAssetType.SAME_TYPE ? (T) referringType : relatedType; + } + static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { - return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> "); + return new EntityTypeRelation<>(REQUIRED, BOOKING_ITEM, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> "); } static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(OPTIONAL, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> "); + return new EntityTypeRelation<>(OPTIONAL, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> "); } static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> "); + return new EntityTypeRelation<>(REQUIRED, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> "); } static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getAssignedToAsset, hostingAssetType, " o..> "); + return new EntityTypeRelation<>(REQUIRED, ASSIGNED_TO_ASSET, HsHostingAssetEntity::getAssignedToAsset, hostingAssetType, " o..> "); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index eea095fb..c263be60 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -60,7 +60,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { - return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX + "$"); + return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + Pattern.quote(IDENTIFIER_SUFFIX) + "$"); } @Override diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java index 53858c29..187630fb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java @@ -20,9 +20,6 @@ import java.util.stream.Stream; import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.FORBIDDEN; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { @@ -34,7 +31,9 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator... properties) { + final HsHostingAssetType assetType, + final AlarmContact alarmContactValidation, + final ValidatableProperty... properties) { super(properties); this.bookingItemReferenceValidation = new ReferenceValidator<>( assetType.bookingItemPolicy(), @@ -73,11 +72,11 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator validateEntityReferencesAndProperties(final HsHostingAssetEntity assetEntity) { return Stream.of( - validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate), - validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate), - validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetReferenceValidation::validate), - validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate), - validateProperties(assetEntity)) + validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate), + validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate), + validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetReferenceValidation::validate), + validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate), + validateProperties(assetEntity)) .filter(Objects::nonNull) .flatMap(List::stream) .filter(Objects::nonNull) @@ -97,25 +96,28 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator optionallyValidate(final HsHostingAssetEntity assetEntity) { return assetEntity != null - ? enrich(prefix(assetEntity.toShortString(), "parentAsset"), - HsHostingAssetEntityValidatorRegistry.forType(assetEntity.getType()).validateContext(assetEntity)) + ? enrich( + prefix(assetEntity.toShortString(), "parentAsset"), + HsHostingAssetEntityValidatorRegistry.forType(assetEntity.getType()).validateContext(assetEntity)) : emptyList(); } private static List optionallyValidate(final HsBookingItemEntity bookingItem) { return bookingItem != null - ? enrich(prefix(bookingItem.toShortString(), "bookingItem"), - HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateContext(bookingItem)) + ? enrich( + prefix(bookingItem.toShortString(), "bookingItem"), + HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateContext(bookingItem)) : emptyList(); } protected List validateAgainstSubEntities(final HsHostingAssetEntity assetEntity) { - return enrich(prefix(assetEntity.toShortString(), "config"), + return enrich( + prefix(assetEntity.toShortString(), "config"), stream(propertyValidators) - .filter(ValidatableProperty::isTotalsValidator) - .map(prop -> validateMaxTotalValue(assetEntity, prop)) - .filter(Objects::nonNull) - .toList()); + .filter(ValidatableProperty::isTotalsValidator) + .map(prop -> validateMaxTotalValue(assetEntity, prop)) + .filter(Objects::nonNull) + .toList()); } // TODO.test: check, if there are any hosting assets which need this validation at all @@ -140,7 +142,9 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { private final HsHostingAssetType.RelationPolicy policy; - private final T subEntityType; - private final Function subEntityGetter; - private final Function subEntityTypeGetter; + private final T referencedEntityType; + private final Function referencedEntityGetter; + private final Function referencedEntityTypeGetter; public ReferenceValidator( final HsHostingAssetType.RelationPolicy policy, final T subEntityType, - final Function subEntityGetter, - final Function subEntityTypeGetter) { + final Function referencedEntityGetter, + final Function referencedEntityTypeGetter) { this.policy = policy; - this.subEntityType = subEntityType; - this.subEntityGetter = subEntityGetter; - this.subEntityTypeGetter = subEntityTypeGetter; + this.referencedEntityType = subEntityType; + this.referencedEntityGetter = referencedEntityGetter; + this.referencedEntityTypeGetter = referencedEntityTypeGetter; } public ReferenceValidator( final HsHostingAssetType.RelationPolicy policy, - final Function subEntityGetter) { + final Function referencedEntityGetter) { this.policy = policy; - this.subEntityType = null; - this.subEntityGetter = subEntityGetter; - this.subEntityTypeGetter = e -> null; + this.referencedEntityType = null; + this.referencedEntityGetter = referencedEntityGetter; + this.referencedEntityTypeGetter = e -> null; } List validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) { - final var subEntity = subEntityGetter.apply(assetEntity); - if (policy == REQUIRED && subEntity == null) { - return List.of(referenceFieldName + "' must not be null but is null"); - } - if ((policy == FORBIDDEN) && (subEntity != null)) { - return List.of(referenceFieldName + "' must be null but is set to "+ assetEntity.getBookingItem().toShortString()); - } - final var subItemType = subEntity != null ? subEntityTypeGetter.apply(subEntity) : null; - if (policy != OPTIONAL && subEntityType != null && subItemType != subEntityType) { - return List.of(referenceFieldName + "' must be of type " + subEntityType + " but is of type " + subItemType); + final var actualEntity = referencedEntityGetter.apply(assetEntity); + final var actualEntityType = actualEntity != null ? referencedEntityTypeGetter.apply(actualEntity) : null; + + switch (policy) { + case REQUIRED: + if (actualEntityType != referencedEntityType) { + return List.of(actualEntityType == null + ? referenceFieldName + "' must be of type " + referencedEntityType + " but is null" + : referenceFieldName + "' must be of type " + referencedEntityType + " but is of type " + actualEntityType); + } + break; + case OPTIONAL: + if (actualEntityType != null && actualEntityType != referencedEntityType) { + return List.of(referenceFieldName + "' must be null or of type " + referencedEntityType + " but is of type " + + actualEntityType); + } + break; + case FORBIDDEN: + if (actualEntityType != null) { + return List.of(referenceFieldName + "' must be null but is of type " + actualEntityType); + } + break; } return emptyList(); } 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 8cd2eb3f..eed85585 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 @@ -186,6 +186,24 @@ public class HsHostingAssetControllerRestTest { } ] """), + DOMAIN_SETUP( + List.of( + HsHostingAssetEntity.builder() + .type(HsHostingAssetType.DOMAIN_SETUP) + .identifier("example.org") + .caption("some fake Domain-Setup") + .build()), + """ + [ + { + "type": "DOMAIN_SETUP", + "identifier": "example.org", + "caption": "some fake Domain-Setup", + "alarmContact": null, + "config": {} + } + ] + """), DOMAIN_DNS_SETUP( List.of( HsHostingAssetEntity.builder() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java index 69fe01bb..8361c5f4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java @@ -33,7 +33,7 @@ class HsCloudServerHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'CLOUD_SERVER:vm1234.bookingItem' must not be null but is null", + "'CLOUD_SERVER:vm1234.bookingItem' must be of type CLOUD_SERVER but is null", "'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'"); } @@ -84,14 +84,14 @@ class HsCloudServerHostingAssetValidatorUnitTest { } @Test - void validatesParentAndAssignedToAssetMustNotBeSet() { + void rejectsInvalidReferencedEntities() { // given final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder() .type(CLOUD_SERVER) - .identifier("xyz00") - .parentAsset(HsHostingAssetEntity.builder().build()) - .assignedToAsset(HsHostingAssetEntity.builder().build()) + .identifier("vm1234") .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) + .parentAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(CLOUD_SERVER).build()) .build(); final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType()); @@ -100,7 +100,7 @@ class HsCloudServerHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'CLOUD_SERVER:xyz00.parentAsset' must be null but is set to D-???????-?:null", - "'CLOUD_SERVER:xyz00.assignedToAsset' must be null but is set to D-???????-?:null"); + "'CLOUD_SERVER:vm1234.parentAsset' must be null but is of type MANAGED_SERVER", + "'CLOUD_SERVER:vm1234.assignedToAsset' must be null but is of type CLOUD_SERVER"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index 6a4e1026..681196ae 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -95,7 +95,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( - "'identifier' expected to match '^example.org|DNS$', but is 'example.org'" + "'identifier' expected to match '^example.org\\Q|DNS\\E$', but is 'example.org'" ); } @@ -113,12 +113,12 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { } @Test - void validatesReferencedEntities() { + void rejectsInvalidReferencedEntities() { // given final var mangedServerHostingAssetEntity = validEntityBuilder() - .parentAsset(HsHostingAssetEntity.builder().build()) - .assignedToAsset(HsHostingAssetEntity.builder().build()) .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) + .parentAsset(null) + .assignedToAsset(HsHostingAssetEntity.builder().type(DOMAIN_SETUP).build()) .build(); final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType()); @@ -127,9 +127,9 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'DOMAIN_DNS_SETUP:example.org|DNS.bookingItem' must be null but is set to D-???????-?:null", - "'DOMAIN_DNS_SETUP:example.org|DNS.parentAsset' must be of type DOMAIN_SETUP but is of type null", - "'DOMAIN_DNS_SETUP:example.org|DNS.assignedToAsset' must be null but is set to D-???????-?:null"); + "'DOMAIN_DNS_SETUP:example.org|DNS.bookingItem' must be null but is of type CLOUD_SERVER", + "'DOMAIN_DNS_SETUP:example.org|DNS.parentAsset' must be of type DOMAIN_SETUP but is null", + "'DOMAIN_DNS_SETUP:example.org|DNS.assignedToAsset' must be null but is of type DOMAIN_SETUP"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java new file mode 100644 index 00000000..c9b99784 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java @@ -0,0 +1,112 @@ +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 org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.Map; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; +import static org.assertj.core.api.Assertions.assertThat; + +class HsDomainSetupHostingAssetValidatorUnitTest { + + static HsHostingAssetEntityBuilder validEntityBuilder() { + return HsHostingAssetEntity.builder() + .type(DOMAIN_SETUP) + .identifier("example.org"); + } + + enum InvalidDomainNameIdentifier { + EMPTY(""), + TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"), + DASH_AT_BEGINNING("-example.com"), + DOT_AT_BEGINNING(".example.com"), + DOT_AT_END("example.com."); + + final String domainName; + + InvalidDomainNameIdentifier(final String domainName) { + this.domainName = domainName; + } + } + + @ParameterizedTest + @EnumSource(InvalidDomainNameIdentifier.class) + void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) { + // given + final var givenEntity = validEntityBuilder().identifier(testCase.domainName).build(); + final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).containsExactly( + "'identifier' expected to match '^((?!-)[A-Za-z0-9-]{1,63}(?