From f5802ed404f9e2e157a764ce262108fe45817728 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jul 2024 16:59:30 +0200 Subject: [PATCH] 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 ); }