From c191af2ea1005e7228697a0b4429448a91eb99ee Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jul 2024 10:32:41 +0200 Subject: [PATCH] add-ipnumber-validatation (#77) Co-authored-by: Michael Hoennig Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/77 Reviewed-by: Marc Sandlus --- doc/hs-hosting-asset-type-structure.md | 111 ++++++------ .../hs/booking/item/HsBookingItemType.java | 8 +- .../hsadminng/hs/booking/item/Node.java | 4 +- .../hs/hosting/asset/HsHostingAssetType.java | 159 +++++++++++++----- .../HostingAssetEntityValidator.java | 30 ++-- .../HostingAssetEntityValidatorRegistry.java | 2 + .../HsIPv4NumberHostingAssetValidator.java | 26 +++ .../HsIPv6NumberHostingAssetValidator.java | 49 ++++++ .../hostsharing/hsadminng/mapper/Array.java | 6 + .../hs-hosting/hs-hosting-asset-schemas.yaml | 2 + .../7010-hs-hosting-asset.sql | 8 +- .../HsHostingAssetControllerRestTest.java | 38 +++++ ...ingAssetPropsControllerAcceptanceTest.java | 4 +- .../asset/HsHostingAssetTypeUnitTest.java | 149 ++++++++-------- ...gAssetEntityValidatorRegistryUnitTest.java | 4 +- ...v4NumberHostingAssetValidatorUnitTest.java | 120 +++++++++++++ ...v6NumberHostingAssetValidatorUnitTest.java | 120 +++++++++++++ ...DatabaseHostingAssetValidatorUnitTest.java | 13 +- 18 files changed, 669 insertions(+), 184 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidatorUnitTest.java diff --git a/doc/hs-hosting-asset-type-structure.md b/doc/hs-hosting-asset-type-structure.md index f7310316..5fec7cff 100644 --- a/doc/hs-hosting-asset-type-structure.md +++ b/doc/hs-hosting-asset-type-structure.md @@ -1,6 +1,61 @@ ## HostingAsset Type Structure +### Server+Webspace + +```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 +} + +package Hosting #feb28c{ + package Server #99bcdb { + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER + entity HA_IPV4_NUMBER + entity HA_IPV6_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_IPV4_NUMBER o..> HA_CLOUD_SERVER +HA_IPV4_NUMBER o..> HA_MANAGED_SERVER +HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE +HA_IPV6_NUMBER o..> HA_CLOUD_SERVER +HA_IPV6_NUMBER o..> HA_MANAGED_SERVER +HA_IPV6_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 +``` + ### Domain ```plantuml @@ -24,12 +79,6 @@ package Hosting #feb28c{ 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 @@ -42,25 +91,19 @@ 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_DOMAIN_SETUP o..> HA_DOMAIN_SETUP HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP -HA_DOMAIN_DNS_SETUP o..> HA_MANAGED_WEBSPACE +HA_DOMAIN_DNS_SETUP o--> HA_MANAGED_WEBSPACE HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP -HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER +HA_DOMAIN_HTTP_SETUP o--> HA_UNIX_USER HA_DOMAIN_SMTP_SETUP *==> HA_DOMAIN_SETUP -HA_DOMAIN_SMTP_SETUP o..> HA_MANAGED_WEBSPACE +HA_DOMAIN_SMTP_SETUP o--> HA_MANAGED_WEBSPACE HA_DOMAIN_MBOX_SETUP *==> HA_DOMAIN_SETUP -HA_DOMAIN_MBOX_SETUP o..> HA_MANAGED_WEBSPACE +HA_DOMAIN_MBOX_SETUP o--> HA_MANAGED_WEBSPACE HA_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_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 @@ -91,12 +134,6 @@ package Hosting #feb28c{ 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 @@ -109,20 +146,12 @@ 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_MANAGED_WEBSPACE -HA_MARIADB_USER o..> HA_MARIADB_INSTANCE +HA_MARIADB_USER o--> HA_MARIADB_INSTANCE HA_MARIADB_DATABASE *==> HA_MARIADB_USER -HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE -HA_IP_NUMBER o..> HA_CLOUD_SERVER -HA_IP_NUMBER o..> HA_MANAGED_SERVER -HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY @@ -153,12 +182,6 @@ package Hosting #feb28c{ 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 @@ -171,20 +194,12 @@ 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 +HA_PGSQL_USER *==> HA_MANAGED_WEBSPACE +HA_PGSQL_USER o--> HA_PGSQL_INSTANCE +HA_PGSQL_DATABASE *==> HA_PGSQL_USER package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY 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 8b51def8..eee5c1eb 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,6 +1,7 @@ package net.hostsharing.hsadminng.hs.booking.item; import java.util.List; +import java.util.Set; import static java.util.Optional.ofNullable; @@ -21,12 +22,17 @@ public enum HsBookingItemType implements Node { } @Override - public List edges() { + public List edges(final Set inGroups) { return ofNullable(parentItemType) .map(p -> (nodeName() + " *--> " + p.nodeName())) .stream().toList(); } + @Override + public boolean belongsToAny(final Set groups) { + return true; // we currently do not filter booking item types + } + @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 index cca14f5a..139fa05f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/Node.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/Node.java @@ -1,9 +1,11 @@ package net.hostsharing.hsadminng.hs.booking.item; import java.util.List; +import java.util.Set; public interface Node { String nodeName(); - List edges(); + boolean belongsToAny(Set groups); + List edges(final Set inGroup); } 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 6d9fa75e..747133d4 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 @@ -10,6 +10,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; @@ -18,7 +19,11 @@ 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.EntityTypeRelation.assignedTo; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.optionalParent; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.optionallyAssignedTo; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.requiredParent; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.requires; 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; @@ -106,11 +111,14 @@ public enum HsHostingAssetType implements Node { inGroup("MariaDB"), requiredParent(MARIADB_USER)), // thus, the MARIADB_USER:Agent becomes RBAC owner - IP_NUMBER( + IPV4_NUMBER( inGroup("Server"), - assignedTo(CLOUD_SERVER), - assignedTo(MANAGED_SERVER), - assignedTo(MANAGED_WEBSPACE) + optionallyAssignedTo(CLOUD_SERVER).or(MANAGED_SERVER).or(MANAGED_WEBSPACE) + ), + + IPV6_NUMBER( + inGroup("Server"), + optionallyAssignedTo(CLOUD_SERVER).or(MANAGED_SERVER).or(MANAGED_WEBSPACE) ); private final String groupName; @@ -144,44 +152,50 @@ public enum HsHostingAssetType implements Node { .orElse(RelationPolicy.FORBIDDEN); } - public HsBookingItemType bookingItemType() { + public Set bookingItemTypes() { return stream(relations) .filter(r -> r.relationType == BOOKING_ITEM) - .map(r -> HsBookingItemType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) - .orElse(null); + .map(r -> r.relatedTypes(this)) + .stream().flatMap(Set::stream) + .map(r -> (HsBookingItemType) r) + .collect(toSet()); } public RelationPolicy parentAssetPolicy() { return stream(relations) .filter(r -> r.relationType == PARENT_ASSET) - .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) + .map(r -> r.relationPolicy) .orElse(RelationPolicy.FORBIDDEN); } - public HsHostingAssetType parentAssetType() { + public Set parentAssetTypes() { return stream(relations) .filter(r -> r.relationType == PARENT_ASSET) - .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) - .orElse(null); + .map(r -> r.relatedTypes(this)) + .stream().flatMap(Set::stream) + .map(r -> (HsHostingAssetType) r) + .collect(toSet()); } public RelationPolicy assignedToAssetPolicy() { return stream(relations) .filter(r -> r.relationType == ASSIGNED_TO_ASSET) - .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) + .map(r -> r.relationPolicy) .orElse(RelationPolicy.FORBIDDEN); } - public HsHostingAssetType assignedToAssetType() { + public Set assignedToAssetTypes() { return stream(relations) .filter(r -> r.relationType == ASSIGNED_TO_ASSET) - .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) - .orElse(null); + .map(r -> r.relatedTypes(this)) + .stream().flatMap(Set::stream) + .map(r -> (HsHostingAssetType) r) + .collect(toSet()); } private static X onlyASingleElementExpectedException(Object a, Object b) { @@ -189,12 +203,22 @@ public enum HsHostingAssetType implements Node { } @Override - public List edges() { + public List edges(final Set inGroups) { return stream(relations) - .map(r -> nodeName() + r.edge + r.relatedType(this).nodeName()) + .map(r -> r.relatedTypes(this).stream() + .filter(x -> x.belongsToAny(inGroups)) + .map(x -> nodeName() + r.edge + x.nodeName()) + .toList()) + .flatMap(List::stream) + .sorted() .toList(); } + @Override + public boolean belongsToAny(final Set groups) { + return groups.contains(this.groupName); + } + @Override public String nodeName() { return "HA_" + name(); @@ -220,12 +244,12 @@ public enum HsHostingAssetType implements Node { .map(t -> "entity " + t.nodeName()) .collect(joining("\n")); final String bookingItemEdges = stream(HsBookingItemType.values()) - .map(HsBookingItemType::edges) + .map(t -> t.edges(includedHostingGroups)) .flatMap(Collection::stream) .collect(joining("\n")); final String hostingAssetEdges = stream(HsHostingAssetType.values()) - .filter(t -> t.isInGroups(includedHostingGroups)) - .map(HsHostingAssetType::edges) + .filter(t -> t.isInGroups(includedHostingGroups)) + .map(t -> t.edges(includedHostingGroups)) .flatMap(Collection::stream) .collect(joining("\n")); return """ @@ -239,7 +263,7 @@ public enum HsHostingAssetType implements Node { package Booking #feb28c { %{bookingNodes} } - + package Hosting #feb28c{ %{hostingGroups} } @@ -257,12 +281,12 @@ public enum HsHostingAssetType implements Node { Booking -down[hidden]->Legend ``` """ - .replace("%{caption}", caption) - .replace("%{bookingNodes}", bookingNodes) - .replace("%{hostingGroups}", hostingGroups) - .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) - .replace("%{bookingItemEdges}", bookingItemEdges) - .replace("%{hostingAssetEdges}", hostingAssetEdges); + .replace("%{caption}", caption) + .replace("%{bookingNodes}", bookingNodes) + .replace("%{hostingGroups}", hostingGroups) + .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) + .replace("%{bookingItemEdges}", bookingItemEdges) + .replace("%{hostingAssetEdges}", hostingAssetEdges); } private boolean isInGroups(final Set assetGroups) { @@ -291,15 +315,17 @@ public enum HsHostingAssetType implements Node { .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(renderAsPlantUML("Server+Webspace", Set.of("Server", "Webspace"))) + .append(renderAsPlantUML("Domain", Set.of("Domain", "Webspace"))) + .append(renderAsPlantUML("MariaDB", Set.of("MariaDB", "Webspace"))) + .append(renderAsPlantUML("PostgreSQL", Set.of("PostgreSQL", "Webspace"))); markdown.append(""" This code generated was by %{this}.main, do not amend manually. """ - .replace("%{this}", HsHostingAssetType.class.getSimpleName())); + .replace("%{this}", HsHostingAssetType.class.getSimpleName())); return markdown.toString(); } @@ -317,8 +343,8 @@ public enum HsHostingAssetType implements Node { public enum RelationType { BOOKING_ITEM, - PARENT_ASSET, - ASSIGNED_TO_ASSET + PARENT_ASSET, + ASSIGNED_TO_ASSET } } @@ -328,27 +354,78 @@ class EntityTypeRelation { final HsHostingAssetType.RelationPolicy relationPolicy; final HsHostingAssetType.RelationType relationType; final Function getter; - private final T relatedType; + private final List acceptedRelatedTypes; final String edge; - public T relatedType(final HsHostingAssetType referringType) { + private EntityTypeRelation( + final HsHostingAssetType.RelationPolicy relationPolicy, + final HsHostingAssetType.RelationType relationType, + final Function getter, + final T acceptedRelatedType, + final String edge + ) { + this(relationPolicy, relationType, getter, modifiyableListOf(acceptedRelatedType), edge); + } + + public Set relatedTypes(final HsHostingAssetType referringType) { + final Set result = acceptedRelatedTypes.stream() + .map(t -> t == HsHostingAssetType.SAME_TYPE ? referringType : t) + .collect(toSet()); //noinspection unchecked - return relatedType == HsHostingAssetType.SAME_TYPE ? (T) referringType : relatedType; + return (Set) result; } static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { - return new EntityTypeRelation<>(REQUIRED, BOOKING_ITEM, HsHostingAssetEntity::getBookingItem, 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..> "); + 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, " *==> "); + 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..> "); + return new EntityTypeRelation<>( + REQUIRED, + ASSIGNED_TO_ASSET, + HsHostingAssetEntity::getAssignedToAsset, + hostingAssetType, + " o--> "); + } + + EntityTypeRelation or(final T alternativeHostingAssetType) { + acceptedRelatedTypes.add(alternativeHostingAssetType); + return this; + } + + static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>( + OPTIONAL, + ASSIGNED_TO_ASSET, + HsHostingAssetEntity::getAssignedToAsset, + hostingAssetType, + " o..> "); + } + + private static ArrayList modifiyableListOf(final T acceptedRelatedType) { + return new ArrayList<>(List.of(acceptedRelatedType)); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java index 7257f70c..6433814c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java @@ -12,9 +12,11 @@ import net.hostsharing.hsadminng.hs.validation.ValidatableProperty; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Arrays.stream; @@ -37,17 +39,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator( assetType.bookingItemPolicy(), - assetType.bookingItemType(), + assetType.bookingItemTypes(), HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType); this.parentAssetReferenceValidation = new ReferenceValidator<>( assetType.parentAssetPolicy(), - assetType.parentAssetType(), + assetType.parentAssetTypes(), HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType); this.assignedToAssetReferenceValidation = new ReferenceValidator<>( assetType.assignedToAssetPolicy(), - assetType.assignedToAssetType(), + assetType.assignedToAssetTypes(), HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType); this.alarmContactValidation = alarmContactValidation; @@ -154,17 +156,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator { private final HsHostingAssetType.RelationPolicy policy; - private final T referencedEntityType; + private final Set referencedEntityTypes; private final Function referencedEntityGetter; private final Function referencedEntityTypeGetter; public ReferenceValidator( final HsHostingAssetType.RelationPolicy policy, - final T subEntityType, + final Set referencedEntityTypes, final Function referencedEntityGetter, final Function referencedEntityTypeGetter) { this.policy = policy; - this.referencedEntityType = subEntityType; + this.referencedEntityTypes = referencedEntityTypes; this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityTypeGetter = referencedEntityTypeGetter; } @@ -173,7 +175,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator referencedEntityGetter) { this.policy = policy; - this.referencedEntityType = null; + this.referencedEntityTypes = Set.of(); this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityTypeGetter = e -> null; } @@ -185,15 +187,15 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator referencedEntityTypes) { + return referencedEntityTypes.stream().sorted().map(Object::toString).collect(Collectors.joining(" or ")); + } } static class AlarmContact extends ReferenceValidator> { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java index 4456a751..696beafe 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java @@ -32,6 +32,8 @@ public class HostingAssetEntityValidatorRegistry { register(PGSQL_INSTANCE, new HsPostgreSqlDbInstanceHostingAssetValidator()); register(PGSQL_USER, new HsPostgreSqlUserHostingAssetValidator()); register(PGSQL_DATABASE, new HsPostgreSqlDatabaseHostingAssetValidator()); + register(IPV4_NUMBER, new HsIPv4NumberHostingAssetValidator()); + register(IPV6_NUMBER, new HsIPv6NumberHostingAssetValidator()); } private static void register(final Enum type, final HsEntityValidator validator) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java new file mode 100644 index 00000000..235a32c2 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java @@ -0,0 +1,26 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; + +import java.util.regex.Pattern; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; + +class HsIPv4NumberHostingAssetValidator extends HostingAssetEntityValidator { + + private static final Pattern IPV4_REGEX = Pattern.compile("^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$"); + + HsIPv4NumberHostingAssetValidator() { + super( + IPV4_NUMBER, + AlarmContact.isOptional(), + + NO_EXTRA_PROPERTIES + ); + } + + @Override + protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + return IPV4_REGEX; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java new file mode 100644 index 00000000..b910ea82 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidator.java @@ -0,0 +1,49 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.regex.Pattern; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV6_NUMBER; + +class HsIPv6NumberHostingAssetValidator extends HostingAssetEntityValidator { + + // simplified pattern, the real check is done by letting Java parse the address + private static final Pattern IPV6_REGEX = Pattern.compile("([a-f0-9:]+:+)+[a-f0-9]+"); + + HsIPv6NumberHostingAssetValidator() { + super( + IPV6_NUMBER, + AlarmContact.isOptional(), + + NO_EXTRA_PROPERTIES + ); + } + + @Override + public List validateEntity(final HsHostingAssetEntity assetEntity) { + final var violations = super.validateEntity(assetEntity); + + if (!isValidIPv6Address(assetEntity.getIdentifier())) { + violations.add("'identifier' expected to be a valid IPv6 address, but is '" + assetEntity.getIdentifier() + "'"); + } + + return violations; + } + + @Override + protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + return IPV6_REGEX; + } + + private boolean isValidIPv6Address(final String identifier) { + try { + return InetAddress.getByName(identifier) instanceof java.net.Inet6Address; + } catch (UnknownHostException e) { + return false; + } + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Array.java b/src/main/java/net/hostsharing/hsadminng/mapper/Array.java index 80970aa4..7f3e32d0 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Array.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Array.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.mapper; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -51,6 +52,11 @@ public class Array { return of(); } + public static T[] emptyArray(final Class elementClass) { + //noinspection unchecked + return (T[]) java.lang.reflect.Array.newInstance(elementClass, 0); + } + @SafeVarargs public static T[] insertNewEntriesAfterExistingEntry(final T[] array, final T entryToFind, final T... newEntries) { final var arrayList = new ArrayList<>(asList(array)); diff --git a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml index b531fe8a..b65a8a51 100644 --- a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml +++ b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml @@ -23,6 +23,8 @@ components: - MARIADB_INSTANCE - MARIADB_USER - MARIADB_DATABASE + - IPV4_NUMBER + - IPV6_NUMBER HsHostingAsset: type: object diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index 4497b675..b54629ee 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -21,7 +21,9 @@ create type HsHostingAssetType as enum ( 'PGSQL_DATABASE', 'MARIADB_INSTANCE', 'MARIADB_USER', - 'MARIADB_DATABASE' + 'MARIADB_DATABASE', + 'IPV4_NUMBER', + 'IPV6_NUMBER' ); CREATE CAST (character varying as HsHostingAssetType) WITH INOUT AS IMPLICIT; @@ -85,6 +87,10 @@ begin when 'MARIADB_INSTANCE' then 'MANAGED_SERVER' when 'MARIADB_USER' then 'MANAGED_WEBSPACE' when 'MARIADB_DATABASE' then 'MARIADB_USER' + + when 'IPV4_NUMBER' then null + when 'IPV6_NUMBER' then null + else raiseException(format('[400] unknown asset type %s', NEW.type::text)) end); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 15c27420..f20006c2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -488,6 +488,44 @@ public class HsHostingAssetControllerRestTest { } } ] + """), + IPV4_NUMBER( + List.of( + HsHostingAssetEntity.builder() + .type(HsHostingAssetType.IPV4_NUMBER) + .assignedToAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) + .identifier("11.12.13.14") + .caption("some fake IPv4 number") + .build()), + """ + [ + { + "type": "IPV4_NUMBER", + "identifier": "11.12.13.14", + "caption": "some fake IPv4 number", + "alarmContact": null, + "config": {} + } + ] + """), + IPV6_NUMBER( + List.of( + HsHostingAssetEntity.builder() + .type(HsHostingAssetType.IPV6_NUMBER) + .assignedToAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) + .identifier("2001:db8:3333:4444:5555:6666:7777:8888") + .caption("some fake IPv6 number") + .build()), + """ + [ + { + "type": "IPV6_NUMBER", + "identifier": "2001:db8:3333:4444:5555:6666:7777:8888", + "caption": "some fake IPv6 number", + "alarmContact": null, + "config": {} + } + ] """); final HsHostingAssetType assetType; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java index dfce271b..6b9188e6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java @@ -47,7 +47,9 @@ class HsHostingAssetPropsControllerAcceptanceTest { "MARIADB_DATABASE", "PGSQL_INSTANCE", "PGSQL_USER", - "PGSQL_DATABASE" + "PGSQL_DATABASE", + "IPV4_NUMBER", + "IPV6_NUMBER" ] """)); // @formatter:on diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java index 09d9537a..c0b97d1f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java @@ -14,19 +14,62 @@ class HsHostingAssetTypeUnitTest { ## HostingAsset Type Structure - ### Domain - + ### Webspace+Server + ```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 } - + + package Hosting #feb28c{ + package Server #99bcdb { + entity HA_CLOUD_SERVER + entity HA_MANAGED_SERVER + entity HA_IPV4_NUMBER + entity HA_IPV6_NUMBER + } + + } + + 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_IPV4_NUMBER o..> HA_CLOUD_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_SERVER + HA_IPV6_NUMBER o..> HA_CLOUD_SERVER + HA_IPV6_NUMBER o..> HA_MANAGED_SERVER + + 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 + ``` + + ### 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 + } + package Hosting #feb28c{ package Domain #99bcdb { entity HA_DOMAIN_SETUP @@ -36,45 +79,33 @@ class HsHostingAssetTypeUnitTest { entity HA_DOMAIN_MBOX_SETUP entity HA_EMAIL_ADDRESS } - - package Server #99bcdb { - entity HA_CLOUD_SERVER - entity HA_MANAGED_SERVER - entity HA_IP_NUMBER - } - + package Webspace #99bcdb { entity HA_MANAGED_WEBSPACE entity HA_UNIX_USER 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_DOMAIN_SETUP o..> HA_DOMAIN_SETUP HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_DNS_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_DNS_SETUP o--> HA_MANAGED_WEBSPACE HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER + HA_DOMAIN_HTTP_SETUP o--> HA_UNIX_USER HA_DOMAIN_SMTP_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_SMTP_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_SMTP_SETUP o--> HA_MANAGED_WEBSPACE HA_DOMAIN_MBOX_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_MBOX_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_MBOX_SETUP o--> HA_MANAGED_WEBSPACE HA_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_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 @@ -83,59 +114,46 @@ class HsHostingAssetTypeUnitTest { } 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 } - + 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_MANAGED_WEBSPACE - HA_MARIADB_USER o..> HA_MARIADB_INSTANCE + HA_MARIADB_USER o--> HA_MARIADB_INSTANCE HA_MARIADB_DATABASE *==> HA_MARIADB_USER - 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 @@ -144,59 +162,46 @@ class HsHostingAssetTypeUnitTest { } 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 } - + 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_MANAGED_WEBSPACE - HA_PGSQL_USER o..> HA_PGSQL_INSTANCE + HA_PGSQL_USER o--> HA_PGSQL_INSTANCE HA_PGSQL_DATABASE *==> HA_PGSQL_USER - 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 @@ -205,7 +210,7 @@ class HsHostingAssetTypeUnitTest { } 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/HostingAssetEntityValidatorRegistryUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java index 4b752663..f6f9a510 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java @@ -45,7 +45,9 @@ class HostingAssetEntityValidatorRegistryUnitTest { HsHostingAssetType.MARIADB_DATABASE, HsHostingAssetType.PGSQL_INSTANCE, HsHostingAssetType.PGSQL_USER, - HsHostingAssetType.PGSQL_DATABASE + HsHostingAssetType.PGSQL_DATABASE, + HsHostingAssetType.IPV4_NUMBER, + HsHostingAssetType.IPV6_NUMBER ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java new file mode 100644 index 00000000..0d219ad2 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java @@ -0,0 +1,120 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Map; + +import static java.util.Map.entry; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; +import static org.assertj.core.api.Assertions.assertThat; + +class HsIPv4NumberHostingAssetValidatorUnitTest { + + static HsHostingAssetEntityBuilder validEntityBuilder() { + return HsHostingAssetEntity.builder() + .type(IPV4_NUMBER) + .identifier("83.223.95.145"); + } + + @Test + void containsExpectedProperties() { + // when + final var validator = HostingAssetEntityValidatorRegistry.forType(IPV4_NUMBER); + + // then + assertThat(validator.properties()).map(Map::toString).isEmpty(); + } + + @Test + void acceptsValidEntity() { + // given + final var givenEntity = validEntityBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).isEmpty(); + } + + @ParameterizedTest + @ValueSource(strings = {"a.b.c.d", "83.223.95", "83.223.95.145.1", "2a01:37:1000::53df:5f91:0"}) + void rejectsInvalidIdentifier(final String givenIdentifier) { + // given + final var givenEntity = validEntityBuilder().identifier(givenIdentifier).build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).containsExactly( + "'identifier' expected to match '^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$', but is '" + givenIdentifier + "'" + ); + } + + @ParameterizedTest + @EnumSource(value = HsHostingAssetType.class, names = { "CLOUD_SERVER", "MANAGED_SERVER", "MANAGED_WEBSPACE" }) + void acceptsValidReferencedEntity(final HsHostingAssetType givenAssignedToAssetType) { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .assignedToAsset(HsHostingAssetEntity.builder().type(givenAssignedToAssetType).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).isEmpty(); + } + + @Test + void rejectsInvalidReferencedEntities() { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) + .parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(UNIX_USER).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'IPV4_NUMBER:83.223.95.145.bookingItem' must be null but is of type CLOUD_SERVER", + "'IPV4_NUMBER:83.223.95.145.parentAsset' must be null but is of type MANAGED_WEBSPACE", + "'IPV4_NUMBER:83.223.95.145.assignedToAsset' must be null or of type CLOUD_SERVER or MANAGED_SERVER or MANAGED_WEBSPACE but is of type UNIX_USER"); + } + + @Test + void rejectsInvalidProperties() { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .config(Map.ofEntries( + entry("any", "false") + )) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'IPV4_NUMBER:83.223.95.145.config.any' is not expected but is set to 'false'"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidatorUnitTest.java new file mode 100644 index 00000000..51d86986 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv6NumberHostingAssetValidatorUnitTest.java @@ -0,0 +1,120 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Map; + +import static java.util.Map.entry; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV6_NUMBER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; +import static org.assertj.core.api.Assertions.assertThat; + +class HsIPv6NumberHostingAssetValidatorUnitTest { + + static HsHostingAssetEntityBuilder validEntityBuilder() { + return HsHostingAssetEntity.builder() + .type(IPV6_NUMBER) + .identifier("2001:db8:3333:4444:5555:6666:7777:8888"); + } + + @Test + void containsExpectedProperties() { + // when + final var validator = HostingAssetEntityValidatorRegistry.forType(IPV6_NUMBER); + + // then + assertThat(validator.properties()).map(Map::toString).isEmpty(); + } + + @Test + void acceptsValidEntity() { + // given + final var givenEntity = validEntityBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).isEmpty(); + } + + @ParameterizedTest + @ValueSource(strings = {"83.223.95", "2a01:37:1000::53df:5f91:0:123::123"}) + void rejectsInvalidIdentifier(final String givenIdentifier) { + // given + final var givenEntity = validEntityBuilder().identifier(givenIdentifier).build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).contains( + "'identifier' expected to be a valid IPv6 address, but is '" + givenIdentifier + "'" + ); + } + + @ParameterizedTest + @EnumSource(value = HsHostingAssetType.class, names = { "CLOUD_SERVER", "MANAGED_SERVER", "MANAGED_WEBSPACE" }) + void acceptsValidReferencedEntity(final HsHostingAssetType givenAssignedToAssetType) { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .assignedToAsset(HsHostingAssetEntity.builder().type(givenAssignedToAssetType).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).isEmpty(); + } + + @Test + void rejectsInvalidReferencedEntities() { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) + .parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(UNIX_USER).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'IPV6_NUMBER:2001:db8:3333:4444:5555:6666:7777:8888.bookingItem' must be null but is of type CLOUD_SERVER", + "'IPV6_NUMBER:2001:db8:3333:4444:5555:6666:7777:8888.parentAsset' must be null but is of type MANAGED_WEBSPACE", + "'IPV6_NUMBER:2001:db8:3333:4444:5555:6666:7777:8888.assignedToAsset' must be null or of type CLOUD_SERVER or MANAGED_SERVER or MANAGED_WEBSPACE but is of type UNIX_USER"); + } + + @Test + void rejectsInvalidProperties() { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .config(Map.ofEntries( + entry("any", "false") + )) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'IPV6_NUMBER:2001:db8:3333:4444:5555:6666:7777:8888.config.any' is not expected but is set to 'false'"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java index 092c253b..35780466 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java @@ -31,7 +31,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { .type(PGSQL_USER) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .assignedToAsset(GIVEN_PGSQL_INSTANCE) - .identifier("xyz00_temp") + .identifier("xyz00_user") .caption("some valid test PgSql-User") .config(new HashMap<>(ofEntries( entry("password", "Hallo Datenbank, lass mich rein!") @@ -42,7 +42,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { return HsHostingAssetEntity.builder() .type(PGSQL_DATABASE) .parentAsset(GIVEN_PGSQL_USER) - .identifier("xyz00_temp") + .identifier("xyz00_db") .caption("some valid test PgSql-Database") .config(new HashMap<>(ofEntries( entry("encoding", "LATIN1") @@ -94,8 +94,9 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", - "'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" + "'PGSQL_DATABASE:xyz00_db.bookingItem' must be null but is of type CLOUD_SERVER", + "'PGSQL_DATABASE:xyz00_db.parentAsset' must be of type PGSQL_USER but is of type PGSQL_INSTANCE", + "'PGSQL_DATABASE:xyz00_db.assignedToAsset' must be null but is of type PGSQL_INSTANCE" ); } @@ -115,8 +116,8 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", - "'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" + "'PGSQL_DATABASE:xyz00_db.config.unknown' is not expected but is set to 'wrong'", + "'PGSQL_DATABASE:xyz00_db.config.encoding' is expected to be of type String, but is of type Integer" ); }