diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md new file mode 100644 index 00000000..b03b7ced --- /dev/null +++ b/doc/hs-hosting-asset-type-structure.md @@ -0,0 +1,199 @@ +## 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/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemType.java index 719ce75b..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 @@ -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/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index 80f9294c..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 @@ -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; @@ -204,11 +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 88ccca45..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 @@ -1,33 +1,204 @@ 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; -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 javax.naming.NamingException; +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 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; +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 + + 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"), + optionalParent(SAME_TYPE) + ), + + DOMAIN_DNS_SETUP( // named e.g. example.org + inGroup("Domain"), + requiredParent(DOMAIN_SETUP)), + + DOMAIN_HTTP_SETUP( // named e.g. example.org + inGroup("Domain"), + requiredParent(DOMAIN_SETUP), + assignedTo(UNIX_USER)), + + DOMAIN_EMAIL_SUBMISSION_SETUP( // named e.g. example.org + inGroup("Domain"), + requiredParent(DOMAIN_SETUP), + assignedTo(MANAGED_WEBSPACE)), + + DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org + inGroup("Domain"), + requiredParent(DOMAIN_SETUP), + assignedTo(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); + this.groupName = null; + this.relations = null; + } + + /// just syntactic sugar + private static String inGroup(final String groupName) { + return groupName; + } + + // TODO.refa: try to get rid of the following similar methods: + + public RelationPolicy bookingItemPolicy() { + 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.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.relationType == PARENT_ASSET) + .map(r -> r.relationPolicy) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) + .orElse(RelationPolicy.FORBIDDEN); + } + + public HsHostingAssetType parentAssetType() { + 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.relationType == ASSIGNED_TO_ASSET) + .map(r -> r.relationPolicy) + .reduce(HsHostingAssetType::onlyASingleElementExpectedException) + .orElse(RelationPolicy.FORBIDDEN); + } + + public HsHostingAssetType assignedToAssetType() { + return stream(relations) + .filter(r -> r.relationType == ASSIGNED_TO_ASSET) + .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).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(this).nodeName()) + .toList(); + } + + @Override + public String nodeName() { + return "HA_" + name(); } public static > HsHostingAssetType of(final T value) { @@ -37,4 +208,148 @@ public enum HsHostingAssetType { static String asString(final HsHostingAssetType type) { return type == null ? null : type.name(); } + + 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().sorted() + .map(HsHostingAssetType::generateGroup) + .collect(joining("\n")); + final String hostingAssetNodes = stream(HsHostingAssetType.values()) + .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 -> t.isInGroups(includedHostingGroups)) + .map(HsHostingAssetType::edges) + .flatMap(Collection::stream) + .collect(joining("\n")); + 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 + ``` + """ + .replace("%{caption}", caption) + .replace("%{bookingNodes}", bookingNodes) + .replace("%{hostingGroups}", hostingGroups) + .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) + .replace("%{bookingItemEdges}", bookingItemEdges) + .replace("%{hostingAssetEdges}", hostingAssetEdges); + } + + 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 -> group.equals(t.groupName)) + .map(t -> " entity " + t.nodeName()) + .collect(joining("\n")) + + "\n }\n"; + } + + static String renderAsEmbeddedPlantUml() { + + final var markdown = new StringBuilder(""" + ## HostingAsset Type Structure + + """); + + // 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())); + + 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"), + renderAsEmbeddedPlantUml(), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + public enum RelationPolicy { + FORBIDDEN, OPTIONAL, REQUIRED + } + + public enum RelationType { + BOOKING_ITEM, + PARENT_ASSET, + ASSIGNED_TO_ASSET + } +} + +@AllArgsConstructor +class EntityTypeRelation { + + final HsHostingAssetType.RelationPolicy relationPolicy; + final HsHostingAssetType.RelationType relationType; + final Function getter; + 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, BOOKING_ITEM, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> "); + } + + static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(OPTIONAL, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> "); + } + + static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(REQUIRED, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> "); + } + + static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>(REQUIRED, ASSIGNED_TO_ASSET, 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 9144189b..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,17 +1,16 @@ 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.CLOUD_SERVER; + class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator { HsCloudServerHostingAssetValidator() { super( - BookingItem.mustBeOfType(HsBookingItemType.CLOUD_SERVER), - ParentAsset.mustBeNull(), - AssignedToAsset.mustBeNull(), + 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 e09f77ef..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 @@ -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; @@ -10,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; @@ -30,11 +30,11 @@ 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( BookingItem.mustBeNull(), - ParentAsset.mustBeOfType(HsHostingAssetType.DOMAIN_SETUP), - AssignedToAsset.mustBeNull(), + super( + DOMAIN_DNS_SETUP, AlarmContact.isOptional(), integerProperty("TTL").min(0).withDefault(21600), @@ -60,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() + Pattern.quote(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)); } } @@ -77,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")) @@ -99,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/HsDomainSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java index d2693f7e..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,8 +2,11 @@ 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; + class HsDomainSetupHostingAssetValidator extends HsHostingAssetEntityValidator { public static final String DOMAIN_NAME_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(? 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/HsEMailAliasHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java index d151b49d..2f4bf5db 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidator.java @@ -15,9 +15,7 @@ class HsEMailAliasHostingAssetValidator extends HsHostingAssetEntityValidator { public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322 HsEMailAliasHostingAssetValidator() { - super( BookingItem.mustBeNull(), - ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE), - AssignedToAsset.mustBeNull(), + super( HsHostingAssetType.EMAIL_ALIAS, AlarmContact.isOptional(), arrayOf( 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 8508ae1e..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 @@ -9,7 +9,6 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.ValidatableProperty; -import jakarta.validation.constraints.NotNull; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -26,21 +25,31 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator[] 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 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,11 +72,11 @@ 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, "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) @@ -87,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 @@ -130,114 +142,79 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator { + static class ReferenceValidator { - private final Policy policy; - private final T subEntityType; - private final Function subEntityGetter; - private final Function subEntityTypeGetter; + private final HsHostingAssetType.RelationPolicy policy; + private final T referencedEntityType; + private final Function referencedEntityGetter; + private final Function referencedEntityTypeGetter; public ReferenceValidator( - final Policy policy, + 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 Policy policy, - final Function subEntityGetter) { + final HsHostingAssetType.RelationPolicy policy, + final Function referencedEntityGetter) { this.policy = policy; - this.subEntityType = null; - this.subEntityGetter = subEntityGetter; - this.subEntityTypeGetter = e -> null; - } - - enum Policy { - OPTIONAL, FORBIDDEN, REQUIRED + 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 == Policy.REQUIRED && subEntity == null) { - return List.of(referenceFieldName + "' must not be null but is null"); - } - if (policy == 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 (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(); } } - static class BookingItem 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) { - 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 { - - AssignedToAsset(final ReferenceValidator.Policy 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..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 @@ -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_WEBSPACE; + 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_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 309404f6..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 @@ -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.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/main/resources/db/changelog/0-basis/010-context.sql b/src/main/resources/db/changelog/0-basis/010-context.sql index 8ea73f45..25c6c48c 100644 --- a/src/main/resources/db/changelog/0-basis/010-context.sql +++ b/src/main/resources/db/changelog/0-basis/010-context.sql @@ -149,7 +149,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar) declare cleanIdentifier varchar; begin - cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._]+', '', 'g'); + cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._|]+', '', 'g'); return cleanIdentifier; end; $$; 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..019bb0a2 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 @@ -36,9 +36,9 @@ subgraph asset["`**asset**`"] style asset:permissions fill:#dd4901,stroke:white perm:asset:INSERT{{asset:INSERT}} - perm:asset:SELECT{{asset:SELECT}} perm:asset:DELETE{{asset:DELETE}} perm:asset:UPDATE{{asset:UPDATE}} + perm:asset:SELECT{{asset:SELECT}} end end @@ -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 @@ -87,6 +90,7 @@ role:bookingItem:AGENT -.-> 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 @@ -104,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 5b740226..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,8 +50,10 @@ begin hsHostingAssetOWNER(NEW), permissions => array['DELETE'], incomingSuperRoles => array[ + globalADMIN(unassumed()), hsBookingItemADMIN(newBookingItem), - hsHostingAssetADMIN(newParentAsset)] + hsHostingAssetADMIN(newParentAsset)], + userUuids => array[currentUserUuid()] ); perform createRoleWithGrants( @@ -85,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/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/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/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 579257a0..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,9 +131,10 @@ 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 }", "{ 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 }", @@ -158,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() - .caption("some new domain setup") .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(); - context("superuser-alex@hostsharing.net"); + + // ... the creating user can read the new domain setup + context("person-SmithPeter@example.com"); + assertThatAssetIsPersisted(result.returnedValue()); + + // ... 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()); - assertThat(assetRepo.count()).isEqualTo(assetCount + 1); } private void assertThatAssetIsPersisted(final HsHostingAssetEntity saved) { - final var context = + em.clear(); 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()); }); - } } 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/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 671b9452..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 @@ -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\\Q|DNS\\E$', 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 @@ -112,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()); @@ -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 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 @@ -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/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java index b7d78567..c9b99784 100644 --- 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 @@ -12,6 +12,7 @@ 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 { @@ -93,8 +94,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest { void validatesReferencedEntities() { // given final var mangedServerHostingAssetEntity = validEntityBuilder() - .parentAsset(HsHostingAssetEntity.builder().build()) - .assignedToAsset(HsHostingAssetEntity.builder().build()) + .parentAsset(HsHostingAssetEntity.builder().type(CLOUD_SERVER).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build()) .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) .build(); final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType()); @@ -104,8 +105,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'DOMAIN_SETUP:example.org.bookingItem' must be null but is set to D-???????-?:null", - "'DOMAIN_SETUP:example.org.parentAsset' must be null but is set to D-???????-?:null", - "'DOMAIN_SETUP:example.org.assignedToAsset' must be null but is set to D-???????-?:null"); + "'DOMAIN_SETUP:example.org.bookingItem' must be null but is of type CLOUD_SERVER", + "'DOMAIN_SETUP:example.org.parentAsset' must be null or of type DOMAIN_SETUP but is of type CLOUD_SERVER", + "'DOMAIN_SETUP:example.org.assignedToAsset' must be null but is of type MANAGED_SERVER"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java index 6c35078b..38e7564e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsEMailAliasHostingAssetValidatorUnitTest.java @@ -107,8 +107,8 @@ class HsEMailAliasHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'EMAIL_ALIAS:abc00-office.bookingItem' must be null but is set to D-1234500:test project:test project booking item", + "'EMAIL_ALIAS:abc00-office.bookingItem' must be null but is of type MANAGED_SERVER", "'EMAIL_ALIAS:abc00-office.parentAsset' must be of type MANAGED_WEBSPACE but is of type MANAGED_SERVER", - "'EMAIL_ALIAS:abc00-office.assignedToAsset' must be null but is set to D-1234500:test project:test project booking item"); + "'EMAIL_ALIAS:abc00-office.assignedToAsset' must be null but is of type MANAGED_SERVER"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java index fd8d4800..15b2ca97 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java @@ -10,6 +10,7 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM; import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_SERVER_BOOKING_ITEM; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static org.assertj.core.api.Assertions.assertThat; @@ -22,8 +23,8 @@ class HsManagedServerHostingAssetValidatorUnitTest { .type(MANAGED_SERVER) .identifier("vm1234") .bookingItem(TEST_MANAGED_SERVER_BOOKING_ITEM) - .parentAsset(HsHostingAssetEntity.builder().build()) - .assignedToAsset(HsHostingAssetEntity.builder().build()) + .parentAsset(HsHostingAssetEntity.builder().type(CLOUD_SERVER).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(CLOUD_SERVER).build()) .config(Map.ofEntries( entry("monit_max_hdd_usage", "90"), entry("monit_max_cpu_usage", 2), @@ -37,8 +38,8 @@ class HsManagedServerHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'MANAGED_SERVER:vm1234.parentAsset' must be null but is set to D-1234500:test project:test project booking item", - "'MANAGED_SERVER:vm1234.assignedToAsset' must be null but is set to D-1234500:test project:test project booking item", + "'MANAGED_SERVER:vm1234.parentAsset' must be null but is of type CLOUD_SERVER", + "'MANAGED_SERVER:vm1234.assignedToAsset' must be null but is of type CLOUD_SERVER", "'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is expected to be at least 10 but is 2", "'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is expected to be at most 100 but is 101", "'MANAGED_SERVER:vm1234.config.monit_max_hdd_usage' is expected to be of type class java.lang.Integer, but is of type 'String'"); @@ -63,14 +64,14 @@ class HsManagedServerHostingAssetValidatorUnitTest { } @Test - void validatesParentAndAssignedToAssetMustNotBeSet() { + void rejectsInvalidReferencedEntities() { // given final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_SERVER) .identifier("xyz00") - .parentAsset(HsHostingAssetEntity.builder().build()) - .assignedToAsset(HsHostingAssetEntity.builder().build()) .bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM) + .parentAsset(HsHostingAssetEntity.builder().type(CLOUD_SERVER).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build()) .build(); final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType()); @@ -80,7 +81,7 @@ class HsManagedServerHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( "'MANAGED_SERVER:xyz00.bookingItem' must be of type MANAGED_SERVER but is of type CLOUD_SERVER", - "'MANAGED_SERVER:xyz00.parentAsset' must be null but is set to D-1234500:test project:test cloud server booking item", - "'MANAGED_SERVER:xyz00.assignedToAsset' must be null but is set to D-1234500:test project:test cloud server booking item"); + "'MANAGED_SERVER:xyz00.parentAsset' must be null but is of type CLOUD_SERVER", + "'MANAGED_SERVER:xyz00.assignedToAsset' must be null but is of type MANAGED_SERVER"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java index 1d2c6d24..7e353147 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.stream.Stream; 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.MANAGED_WEBSPACE; import static org.assertj.core.api.Assertions.assertThat; import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.TEST_PROJECT; @@ -142,7 +143,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { } @Test - void validatesEntityReferences() { + void rejectsInvalidEntityReferences() { // given final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() @@ -153,7 +154,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { .resources(Map.ofEntries(entry("SSD", 25), entry("Traffic", 250))) .build()) .parentAsset(cloudServerAssetEntity) - .assignedToAsset(HsHostingAssetEntity.builder().build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(CLOUD_SERVER).build()) .identifier("abc00") .build(); @@ -163,7 +164,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { // then assertThat(result).containsExactly( "'MANAGED_WEBSPACE:abc00.bookingItem' must be of type MANAGED_WEBSPACE but is of type MANAGED_SERVER", - "'MANAGED_WEBSPACE:abc00.parentAsset' must be of type MANAGED_SERVER but is of type CLOUD_SERVER", - "'MANAGED_WEBSPACE:abc00.assignedToAsset' must be null but is set to D-???????-?:some ManagedServer"); + "'MANAGED_WEBSPACE:abc00.parentAsset' must be null or of type MANAGED_SERVER but is of type CLOUD_SERVER", + "'MANAGED_WEBSPACE:abc00.assignedToAsset' must be null but is of type CLOUD_SERVER"); } } 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)