add-ipnumber-validatation #77

Merged
hsh-michaelhoennig merged 4 commits from add-ipnumber-validatation into master 2024-07-16 10:32:41 +02:00
13 changed files with 328 additions and 78 deletions
Showing only changes of commit ccd7fead30 - Show all commits

View File

@ -5,11 +5,13 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.booking.item.Node; import net.hostsharing.hsadminng.hs.booking.item.Node;
import jakarta.validation.constraints.NotNull;
import javax.naming.NamingException; import javax.naming.NamingException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -18,12 +20,17 @@ import java.util.function.Function;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet; 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.OPTIONAL;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; 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.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.BOOKING_ITEM;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.PARENT_ASSET; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.PARENT_ASSET;
import static net.hostsharing.hsadminng.mapper.Array.emptyArray;
public enum HsHostingAssetType implements Node { public enum HsHostingAssetType implements Node {
SAME_TYPE, // pseudo-type for recursive references SAME_TYPE, // pseudo-type for recursive references
@ -106,27 +113,25 @@ public enum HsHostingAssetType implements Node {
inGroup("MariaDB"), inGroup("MariaDB"),
requiredParent(MARIADB_USER)), // thus, the MARIADB_USER:Agent becomes RBAC owner requiredParent(MARIADB_USER)), // thus, the MARIADB_USER:Agent becomes RBAC owner
IP_NUMBER( IPV4_NUMBER(
inGroup("Server"), inGroup("Server"),
assignedTo(CLOUD_SERVER), optionallyAssignedTo(CLOUD_SERVER).or(MANAGED_SERVER).or(MANAGED_WEBSPACE)
assignedTo(MANAGED_SERVER),
assignedTo(MANAGED_WEBSPACE)
); );
private final String groupName; private final String groupName;
private final EntityTypeRelation<?, ?>[] relations; private final EntityTypeRelation<?, ?>[] relations;
HsHostingAssetType( HsHostingAssetType(
final String groupName, String groupName,
final EntityTypeRelation<?, ?>... relations @NotNull final EntityTypeRelation<?, ?>... relations
) { ) {
this.groupName = groupName; this.groupName = groupName;
this.relations = relations; this.relations = relations;
} }
HsHostingAssetType() { HsHostingAssetType() {
this.groupName = null; this.groupName = null;
this.relations = null; this.relations = emptyArray(EntityTypeRelation.class);
} }
/// just syntactic sugar /// just syntactic sugar
@ -144,44 +149,50 @@ public enum HsHostingAssetType implements Node {
.orElse(RelationPolicy.FORBIDDEN); .orElse(RelationPolicy.FORBIDDEN);
} }
public HsBookingItemType bookingItemType() { public Set<HsBookingItemType> bookingItemTypes() {
return stream(relations) return stream(relations)
.filter(r -> r.relationType == BOOKING_ITEM) .filter(r -> r.relationType == BOOKING_ITEM)
.map(r -> HsBookingItemType.valueOf(r.relatedType(this).toString()))
.reduce(HsHostingAssetType::onlyASingleElementExpectedException) .reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(null); .map(r -> r.relatedTypes(this))
.stream().flatMap(Set::stream)
.map(r -> (HsBookingItemType) r)
.collect(toSet());
} }
public RelationPolicy parentAssetPolicy() { public RelationPolicy parentAssetPolicy() {
return stream(relations) return stream(relations)
.filter(r -> r.relationType == PARENT_ASSET) .filter(r -> r.relationType == PARENT_ASSET)
.map(r -> r.relationPolicy)
.reduce(HsHostingAssetType::onlyASingleElementExpectedException) .reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.map(r -> r.relationPolicy)
.orElse(RelationPolicy.FORBIDDEN); .orElse(RelationPolicy.FORBIDDEN);
} }
public HsHostingAssetType parentAssetType() { public Set<HsHostingAssetType> parentAssetTypes() {
return stream(relations) return stream(relations)
.filter(r -> r.relationType == PARENT_ASSET) .filter(r -> r.relationType == PARENT_ASSET)
.map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString()))
.reduce(HsHostingAssetType::onlyASingleElementExpectedException) .reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(null); .map(r -> r.relatedTypes(this))
.stream().flatMap(Set::stream)
.map(r -> (HsHostingAssetType) r)
.collect(toSet());
} }
public RelationPolicy assignedToAssetPolicy() { public RelationPolicy assignedToAssetPolicy() {
return stream(relations) return stream(relations)
.filter(r -> r.relationType == ASSIGNED_TO_ASSET) .filter(r -> r.relationType == ASSIGNED_TO_ASSET)
.map(r -> r.relationPolicy)
.reduce(HsHostingAssetType::onlyASingleElementExpectedException) .reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.map(r -> r.relationPolicy)
.orElse(RelationPolicy.FORBIDDEN); .orElse(RelationPolicy.FORBIDDEN);
} }
public HsHostingAssetType assignedToAssetType() { public Set<HsHostingAssetType> assignedToAssetTypes() {
return stream(relations) return stream(relations)
.filter(r -> r.relationType == ASSIGNED_TO_ASSET) .filter(r -> r.relationType == ASSIGNED_TO_ASSET)
.map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString()))
.reduce(HsHostingAssetType::onlyASingleElementExpectedException) .reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(null); .map(r -> r.relatedTypes(this))
.stream().flatMap(Set::stream)
.map(r -> (HsHostingAssetType) r)
.collect(toSet());
} }
private static <X> X onlyASingleElementExpectedException(Object a, Object b) { private static <X> X onlyASingleElementExpectedException(Object a, Object b) {
@ -191,7 +202,9 @@ public enum HsHostingAssetType implements Node {
@Override @Override
public List<String> edges() { public List<String> edges() {
return stream(relations) return stream(relations)
.map(r -> nodeName() + r.edge + r.relatedType(this).nodeName()) .map(r -> r.relatedTypes(this).stream().map(x -> nodeName() + r.edge + x.nodeName()).toList())
.flatMap(List::stream)
.sorted()
.toList(); .toList();
} }
@ -224,7 +237,7 @@ public enum HsHostingAssetType implements Node {
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(joining("\n")); .collect(joining("\n"));
final String hostingAssetEdges = stream(HsHostingAssetType.values()) final String hostingAssetEdges = stream(HsHostingAssetType.values())
.filter(t -> t.isInGroups(includedHostingGroups)) .filter(t -> t.isInGroups(includedHostingGroups))
.map(HsHostingAssetType::edges) .map(HsHostingAssetType::edges)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(joining("\n")); .collect(joining("\n"));
@ -257,12 +270,12 @@ public enum HsHostingAssetType implements Node {
Booking -down[hidden]->Legend Booking -down[hidden]->Legend
``` ```
""" """
.replace("%{caption}", caption) .replace("%{caption}", caption)
.replace("%{bookingNodes}", bookingNodes) .replace("%{bookingNodes}", bookingNodes)
.replace("%{hostingGroups}", hostingGroups) .replace("%{hostingGroups}", hostingGroups)
.replace("%{hostingAssetNodeStyles}", hostingAssetNodes) .replace("%{hostingAssetNodeStyles}", hostingAssetNodes)
.replace("%{bookingItemEdges}", bookingItemEdges) .replace("%{bookingItemEdges}", bookingItemEdges)
.replace("%{hostingAssetEdges}", hostingAssetEdges); .replace("%{hostingAssetEdges}", hostingAssetEdges);
} }
private boolean isInGroups(final Set<String> assetGroups) { private boolean isInGroups(final Set<String> assetGroups) {
@ -299,7 +312,7 @@ public enum HsHostingAssetType implements Node {
This code generated was by %{this}.main, do not amend manually. This code generated was by %{this}.main, do not amend manually.
""" """
.replace("%{this}", HsHostingAssetType.class.getSimpleName())); .replace("%{this}", HsHostingAssetType.class.getSimpleName()));
return markdown.toString(); return markdown.toString();
} }
@ -317,8 +330,8 @@ public enum HsHostingAssetType implements Node {
public enum RelationType { public enum RelationType {
BOOKING_ITEM, BOOKING_ITEM,
PARENT_ASSET, PARENT_ASSET,
ASSIGNED_TO_ASSET ASSIGNED_TO_ASSET
} }
} }
@ -328,27 +341,78 @@ class EntityTypeRelation<E, T extends Node> {
final HsHostingAssetType.RelationPolicy relationPolicy; final HsHostingAssetType.RelationPolicy relationPolicy;
final HsHostingAssetType.RelationType relationType; final HsHostingAssetType.RelationType relationType;
final Function<HsHostingAssetEntity, E> getter; final Function<HsHostingAssetEntity, E> getter;
private final T relatedType; private final List<T> acceptedRelatedTypes;
final String edge; final String edge;
public T relatedType(final HsHostingAssetType referringType) { private EntityTypeRelation(
final HsHostingAssetType.RelationPolicy relationPolicy,
final HsHostingAssetType.RelationType relationType,
final Function<HsHostingAssetEntity, E> getter,
final T acceptedRelatedType,
final String edge
) {
this(relationPolicy, relationType, getter, modifiyableListOf(acceptedRelatedType), edge);
}
public <R extends Node> Set<R> relatedTypes(final HsHostingAssetType referringType) {
final Set<Node> result = acceptedRelatedTypes.stream()
.map(t -> t == HsHostingAssetType.SAME_TYPE ? referringType : t)
.collect(toSet());
//noinspection unchecked //noinspection unchecked
return relatedType == HsHostingAssetType.SAME_TYPE ? (T) referringType : relatedType; return (Set<R>) result;
} }
static EntityTypeRelation<HsBookingItemEntity, HsBookingItemType> requires(final HsBookingItemType bookingItemType) { static EntityTypeRelation<HsBookingItemEntity, HsBookingItemType> requires(final HsBookingItemType bookingItemType) {
return new EntityTypeRelation<>(REQUIRED, BOOKING_ITEM, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> "); return new EntityTypeRelation<>(
REQUIRED,
BOOKING_ITEM,
HsHostingAssetEntity::getBookingItem,
bookingItemType,
" *==> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionalParent(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> 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<HsHostingAssetEntity, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>(REQUIRED, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> "); return new EntityTypeRelation<>(
REQUIRED,
PARENT_ASSET,
HsHostingAssetEntity::getParentAsset,
hostingAssetType,
" *==> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> assignedTo(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> 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<E, T> or(final T alternativeHostingAssetType) {
acceptedRelatedTypes.add(alternativeHostingAssetType);
return this;
}
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionallyAssignedTo(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>(
OPTIONAL,
ASSIGNED_TO_ASSET,
HsHostingAssetEntity::getAssignedToAsset,
hostingAssetType,
" o..> ");
}
private static <T extends Node> ArrayList<T> modifiyableListOf(final T acceptedRelatedType) {
return new ArrayList<>(List.of(acceptedRelatedType));
} }
} }

View File

@ -12,9 +12,11 @@ import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
@ -37,17 +39,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
super(properties); super(properties);
this.bookingItemReferenceValidation = new ReferenceValidator<>( this.bookingItemReferenceValidation = new ReferenceValidator<>(
assetType.bookingItemPolicy(), assetType.bookingItemPolicy(),
assetType.bookingItemType(), assetType.bookingItemTypes(),
HsHostingAssetEntity::getBookingItem, HsHostingAssetEntity::getBookingItem,
HsBookingItemEntity::getType); HsBookingItemEntity::getType);
this.parentAssetReferenceValidation = new ReferenceValidator<>( this.parentAssetReferenceValidation = new ReferenceValidator<>(
assetType.parentAssetPolicy(), assetType.parentAssetPolicy(),
assetType.parentAssetType(), assetType.parentAssetTypes(),
HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getParentAsset,
HsHostingAssetEntity::getType); HsHostingAssetEntity::getType);
this.assignedToAssetReferenceValidation = new ReferenceValidator<>( this.assignedToAssetReferenceValidation = new ReferenceValidator<>(
assetType.assignedToAssetPolicy(), assetType.assignedToAssetPolicy(),
assetType.assignedToAssetType(), assetType.assignedToAssetTypes(),
HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getAssignedToAsset,
HsHostingAssetEntity::getType); HsHostingAssetEntity::getType);
this.alarmContactValidation = alarmContactValidation; this.alarmContactValidation = alarmContactValidation;
@ -154,17 +156,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
static class ReferenceValidator<S, T> { static class ReferenceValidator<S, T> {
private final HsHostingAssetType.RelationPolicy policy; private final HsHostingAssetType.RelationPolicy policy;
private final T referencedEntityType; private final Set<T> referencedEntityTypes;
private final Function<HsHostingAssetEntity, S> referencedEntityGetter; private final Function<HsHostingAssetEntity, S> referencedEntityGetter;
private final Function<S, T> referencedEntityTypeGetter; private final Function<S, T> referencedEntityTypeGetter;
public ReferenceValidator( public ReferenceValidator(
final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType.RelationPolicy policy,
final T subEntityType, final Set<T> referencedEntityTypes,
final Function<HsHostingAssetEntity, S> referencedEntityGetter, final Function<HsHostingAssetEntity, S> referencedEntityGetter,
final Function<S, T> referencedEntityTypeGetter) { final Function<S, T> referencedEntityTypeGetter) {
this.policy = policy; this.policy = policy;
this.referencedEntityType = subEntityType; this.referencedEntityTypes = referencedEntityTypes;
this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityGetter = referencedEntityGetter;
this.referencedEntityTypeGetter = referencedEntityTypeGetter; this.referencedEntityTypeGetter = referencedEntityTypeGetter;
} }
@ -173,7 +175,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType.RelationPolicy policy,
final Function<HsHostingAssetEntity, S> referencedEntityGetter) { final Function<HsHostingAssetEntity, S> referencedEntityGetter) {
this.policy = policy; this.policy = policy;
this.referencedEntityType = null; this.referencedEntityTypes = Set.of();
this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityGetter = referencedEntityGetter;
this.referencedEntityTypeGetter = e -> null; this.referencedEntityTypeGetter = e -> null;
} }
@ -185,15 +187,15 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
switch (policy) { switch (policy) {
case REQUIRED: case REQUIRED:
if (actualEntityType != referencedEntityType) { if (!referencedEntityTypes.contains(actualEntityType)) {
return List.of(actualEntityType == null return List.of(actualEntityType == null
? referenceFieldName + "' must be of type " + referencedEntityType + " but is null" ? referenceFieldName + "' must be of type " + toDisplay(referencedEntityTypes) + " but is null"
: referenceFieldName + "' must be of type " + referencedEntityType + " but is of type " + actualEntityType); : referenceFieldName + "' must be of type " + toDisplay(referencedEntityTypes) + " but is of type " + actualEntityType);
} }
break; break;
case OPTIONAL: case OPTIONAL:
if (actualEntityType != null && actualEntityType != referencedEntityType) { if (actualEntityType != null && !referencedEntityTypes.contains(actualEntityType)) {
return List.of(referenceFieldName + "' must be null or of type " + referencedEntityType + " but is of type " return List.of(referenceFieldName + "' must be null or of type " + toDisplay(referencedEntityTypes) + " but is of type "
+ actualEntityType); + actualEntityType);
} }
break; break;
@ -205,6 +207,10 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
} }
return emptyList(); return emptyList();
} }
private String toDisplay(final Set<T> referencedEntityTypes) {
return referencedEntityTypes.stream().sorted().map(Object::toString).collect(Collectors.joining(" or "));
}
} }
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> { static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {

View File

@ -32,6 +32,7 @@ public class HostingAssetEntityValidatorRegistry {
register(PGSQL_INSTANCE, new HsPostgreSqlDbInstanceHostingAssetValidator()); register(PGSQL_INSTANCE, new HsPostgreSqlDbInstanceHostingAssetValidator());
register(PGSQL_USER, new HsPostgreSqlUserHostingAssetValidator()); register(PGSQL_USER, new HsPostgreSqlUserHostingAssetValidator());
register(PGSQL_DATABASE, new HsPostgreSqlDatabaseHostingAssetValidator()); register(PGSQL_DATABASE, new HsPostgreSqlDatabaseHostingAssetValidator());
register(IPV4_NUMBER, new HsIPv4NumberHostingAssetValidator());
} }
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) { private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {

View File

@ -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 {
public static 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;
}
}

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.mapper; package net.hostsharing.hsadminng.mapper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -51,6 +52,11 @@ public class Array {
return of(); return of();
} }
public static <T> T[] emptyArray(final Class<T> elementClass) {
//noinspection unchecked
return (T[]) java.lang.reflect.Array.newInstance(elementClass, 0);
}
@SafeVarargs @SafeVarargs
public static <T> T[] insertNewEntriesAfterExistingEntry(final T[] array, final T entryToFind, final T... newEntries) { public static <T> T[] insertNewEntriesAfterExistingEntry(final T[] array, final T entryToFind, final T... newEntries) {
final var arrayList = new ArrayList<>(asList(array)); final var arrayList = new ArrayList<>(asList(array));

View File

@ -23,6 +23,7 @@ components:
- MARIADB_INSTANCE - MARIADB_INSTANCE
- MARIADB_USER - MARIADB_USER
- MARIADB_DATABASE - MARIADB_DATABASE
- IPV4_NUMBER
HsHostingAsset: HsHostingAsset:
type: object type: object

View File

@ -21,7 +21,8 @@ create type HsHostingAssetType as enum (
'PGSQL_DATABASE', 'PGSQL_DATABASE',
'MARIADB_INSTANCE', 'MARIADB_INSTANCE',
'MARIADB_USER', 'MARIADB_USER',
'MARIADB_DATABASE' 'MARIADB_DATABASE',
'IPV4_NUMBER'
); );
CREATE CAST (character varying as HsHostingAssetType) WITH INOUT AS IMPLICIT; CREATE CAST (character varying as HsHostingAssetType) WITH INOUT AS IMPLICIT;
@ -85,6 +86,9 @@ begin
when 'MARIADB_INSTANCE' then 'MANAGED_SERVER' when 'MARIADB_INSTANCE' then 'MANAGED_SERVER'
when 'MARIADB_USER' then 'MANAGED_WEBSPACE' when 'MARIADB_USER' then 'MANAGED_WEBSPACE'
when 'MARIADB_DATABASE' then 'MARIADB_USER' when 'MARIADB_DATABASE' then 'MARIADB_USER'
when 'IPV4_NUMBER' then null
else raiseException(format('[400] unknown asset type %s', NEW.type::text)) else raiseException(format('[400] unknown asset type %s', NEW.type::text))
end); end);

View File

@ -488,6 +488,25 @@ 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": {}
}
]
"""); """);
final HsHostingAssetType assetType; final HsHostingAssetType assetType;

View File

@ -47,7 +47,8 @@ class HsHostingAssetPropsControllerAcceptanceTest {
"MARIADB_DATABASE", "MARIADB_DATABASE",
"PGSQL_INSTANCE", "PGSQL_INSTANCE",
"PGSQL_USER", "PGSQL_USER",
"PGSQL_DATABASE" "PGSQL_DATABASE",
"IPV4_NUMBER"
] ]
""")); """));
// @formatter:on // @formatter:on

View File

@ -40,7 +40,7 @@ class HsHostingAssetTypeUnitTest {
package Server #99bcdb { package Server #99bcdb {
entity HA_CLOUD_SERVER entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER entity HA_MANAGED_SERVER
entity HA_IP_NUMBER entity HA_IPV4_NUMBER
} }
package Webspace #99bcdb { package Webspace #99bcdb {
@ -63,17 +63,17 @@ class HsHostingAssetTypeUnitTest {
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP
HA_DOMAIN_DNS_SETUP *==> 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 *==> 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 *==> 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 *==> 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_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_SETUP
HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IPV4_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IPV4_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend #white { package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
@ -107,7 +107,7 @@ class HsHostingAssetTypeUnitTest {
package Server #99bcdb { package Server #99bcdb {
entity HA_CLOUD_SERVER entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER entity HA_MANAGED_SERVER
entity HA_IP_NUMBER entity HA_IPV4_NUMBER
} }
package Webspace #99bcdb { package Webspace #99bcdb {
@ -130,11 +130,11 @@ class HsHostingAssetTypeUnitTest {
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
HA_MARIADB_USER *==> HA_MANAGED_WEBSPACE 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 *==> HA_MARIADB_USER
HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IPV4_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IPV4_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend #white { package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
@ -168,7 +168,7 @@ class HsHostingAssetTypeUnitTest {
package Server #99bcdb { package Server #99bcdb {
entity HA_CLOUD_SERVER entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER entity HA_MANAGED_SERVER
entity HA_IP_NUMBER entity HA_IPV4_NUMBER
} }
package Webspace #99bcdb { package Webspace #99bcdb {
@ -191,11 +191,11 @@ class HsHostingAssetTypeUnitTest {
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER
HA_PGSQL_USER *==> HA_MANAGED_WEBSPACE 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_PGSQL_DATABASE *==> HA_PGSQL_USER
HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IPV4_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IPV4_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend #white { package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY

View File

@ -45,7 +45,8 @@ class HostingAssetEntityValidatorRegistryUnitTest {
HsHostingAssetType.MARIADB_DATABASE, HsHostingAssetType.MARIADB_DATABASE,
HsHostingAssetType.PGSQL_INSTANCE, HsHostingAssetType.PGSQL_INSTANCE,
HsHostingAssetType.PGSQL_USER, HsHostingAssetType.PGSQL_USER,
HsHostingAssetType.PGSQL_DATABASE HsHostingAssetType.PGSQL_DATABASE,
HsHostingAssetType.IPV4_NUMBER
); );
} }
} }

View File

@ -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'");
}
}

View File

@ -31,7 +31,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest {
.type(PGSQL_USER) .type(PGSQL_USER)
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
.assignedToAsset(GIVEN_PGSQL_INSTANCE) .assignedToAsset(GIVEN_PGSQL_INSTANCE)
.identifier("xyz00_temp") .identifier("xyz00_user")
.caption("some valid test PgSql-User") .caption("some valid test PgSql-User")
.config(new HashMap<>(ofEntries( .config(new HashMap<>(ofEntries(
entry("password", "Hallo Datenbank, lass mich rein!") entry("password", "Hallo Datenbank, lass mich rein!")
@ -42,7 +42,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest {
return HsHostingAssetEntity.builder() return HsHostingAssetEntity.builder()
.type(PGSQL_DATABASE) .type(PGSQL_DATABASE)
.parentAsset(GIVEN_PGSQL_USER) .parentAsset(GIVEN_PGSQL_USER)
.identifier("xyz00_temp") .identifier("xyz00_db")
.caption("some valid test PgSql-Database") .caption("some valid test PgSql-Database")
.config(new HashMap<>(ofEntries( .config(new HashMap<>(ofEntries(
entry("encoding", "LATIN1") entry("encoding", "LATIN1")
@ -94,8 +94,9 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", "'PGSQL_DATABASE:xyz00_db.bookingItem' must be null but is of type CLOUD_SERVER",
"'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" "'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 // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", "'PGSQL_DATABASE:xyz00_db.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.encoding' is expected to be of type String, but is of type Integer"
); );
} }