Compare commits
3 Commits
6a66f5f45b
...
5521cc13f8
Author | SHA1 | Date | |
---|---|---|---|
|
5521cc13f8 | ||
|
c19360aa77 | ||
|
278467efac |
@ -14,12 +14,26 @@ public interface HsHostingAssetRepository extends Repository<HsHostingAssetEntit
|
||||
|
||||
List<HsHostingAssetEntity> findByIdentifier(String assetIdentifier);
|
||||
|
||||
@Query("""
|
||||
SELECT asset FROM HsHostingAssetEntity asset
|
||||
WHERE (:projectUuid IS NULL OR asset.bookingItem.project.uuid = :projectUuid)
|
||||
AND (:parentAssetUuid IS NULL OR asset.parentAsset.uuid = :parentAssetUuid)
|
||||
AND (:type IS NULL OR :type = CAST(asset.type AS String))
|
||||
""")
|
||||
@Query(value = """
|
||||
select ha.uuid,
|
||||
ha.alarmcontactuuid,
|
||||
ha.assignedtoassetuuid,
|
||||
ha.bookingitemuuid,
|
||||
ha.caption,
|
||||
ha.config,
|
||||
ha.identifier,
|
||||
ha.parentassetuuid,
|
||||
ha.type,
|
||||
ha.version
|
||||
from hs_hosting_asset_rv ha
|
||||
left join hs_booking_item bi on bi.uuid = ha.bookingitemuuid
|
||||
left join hs_hosting_asset pha on pha.uuid = ha.parentassetuuid
|
||||
where (:projectUuid is null or bi.projectuuid=:projectUuid)
|
||||
and (:parentAssetUuid is null or pha.uuid=:parentAssetUuid)
|
||||
and (:type is null or :type=cast(ha.type as text))
|
||||
""", nativeQuery = true)
|
||||
// The JPQL query did not generate "left join" but just "join".
|
||||
// I also optimized the query by not using the _rv for hs_booking_item and hs_hosting_asset, only for hs_hosting_asset_rv.
|
||||
List<HsHostingAssetEntity> findAllByCriteriaImpl(UUID projectUuid, UUID parentAssetUuid, String type);
|
||||
default List<HsHostingAssetEntity> findAllByCriteria(final UUID projectUuid, final UUID parentAssetUuid, final HsHostingAssetType type) {
|
||||
return findAllByCriteriaImpl(projectUuid, parentAssetUuid, HsHostingAssetType.asString(type));
|
||||
|
@ -12,20 +12,22 @@ import static net.hostsharing.hsadminng.mapper.Array.insertAfterEntries;
|
||||
public class ArrayProperty<P extends ValidatableProperty<?, E>, E> extends ValidatableProperty<ArrayProperty<P, E>, E[]> {
|
||||
|
||||
private static final String[] KEY_ORDER =
|
||||
insertAfterEntries(ValidatableProperty.KEY_ORDER, "required", "minLength" ,"maxLength");
|
||||
private final ValidatableProperty<?, E> elementProperty;
|
||||
insertAfterEntries(
|
||||
insertAfterEntries(ValidatableProperty.KEY_ORDER, "required", "minLength" ,"maxLength"),
|
||||
"propertyName", "elementProperty");
|
||||
private final ValidatableProperty<?, E> elementsOf;
|
||||
private Integer minLength;
|
||||
private Integer maxLength;
|
||||
|
||||
private ArrayProperty(final ValidatableProperty<?, E> elementProperty) {
|
||||
private ArrayProperty(final ValidatableProperty<?, E> elementsOf) {
|
||||
//noinspection unchecked
|
||||
super((Class<E[]>) elementProperty.type.arrayType(), elementProperty.propertyName, KEY_ORDER);
|
||||
this.elementProperty = elementProperty;
|
||||
super((Class<E[]>) elementsOf.type.arrayType(), elementsOf.propertyName, KEY_ORDER);
|
||||
this.elementsOf = elementsOf;
|
||||
}
|
||||
|
||||
public static <T> ArrayProperty<?, T[]> arrayOf(final ValidatableProperty<?, T> elementProperty) {
|
||||
public static <T> ArrayProperty<?, T[]> arrayOf(final ValidatableProperty<?, T> elementsOf) {
|
||||
//noinspection unchecked
|
||||
return (ArrayProperty<?, T[]>) new ArrayProperty<>(elementProperty);
|
||||
return (ArrayProperty<?, T[]>) new ArrayProperty<>(elementsOf);
|
||||
}
|
||||
|
||||
public ValidatableProperty<?, ?> minLength(final int minLength) {
|
||||
@ -46,14 +48,15 @@ public class ArrayProperty<P extends ValidatableProperty<?, E>, E> extends Valid
|
||||
if (maxLength != null && propValue.length > maxLength) {
|
||||
result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length);
|
||||
}
|
||||
stream(propValue).forEach(e -> elementProperty.validate(result, e, propProvider));
|
||||
stream(propValue).forEach(e -> elementsOf.validate(result, e, propProvider));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String simpleTypeName() {
|
||||
return elementProperty.simpleTypeName() + "[]";
|
||||
return elementsOf.simpleTypeName() + "[]";
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private String display(final E... propValue) {
|
||||
return "[" + Arrays.toString(propValue) + "]";
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ public class StringProperty<P extends StringProperty<P>> extends ValidatableProp
|
||||
result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length());
|
||||
}
|
||||
if (matchesRegEx != null &&
|
||||
stream(matchesRegEx).map(p -> p.matcher(propValue)).map(Matcher::matches).findAny().isEmpty()) {
|
||||
stream(matchesRegEx).map(p -> p.matcher(propValue)).noneMatch(Matcher::matches)) {
|
||||
result.add(propertyName + "' is expected to match any of " + Arrays.toString(matchesRegEx) + " but " + display(propValue) + " does not match any");
|
||||
}
|
||||
if (isReadOnly() && propValue != null) {
|
||||
|
@ -1,15 +1,16 @@
|
||||
package net.hostsharing.hsadminng.hs.validation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||
import net.hostsharing.hsadminng.mapper.Array;
|
||||
import org.apache.commons.lang3.function.TriFunction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -22,6 +23,7 @@ import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.apache.commons.lang3.ObjectUtils.isArray;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@ -239,8 +241,8 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
|
||||
}
|
||||
|
||||
private Object arrayToList(final Object value) {
|
||||
if ( value instanceof String[]) {
|
||||
return List.of((String[])value);
|
||||
if (isArray(value)) {
|
||||
return Arrays.stream((Object[])value).map(Object::toString).toList();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@ -265,4 +267,9 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
|
||||
public <E extends PropertiesProvider> T compute(final E entity) {
|
||||
return computedBy.apply(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toOrderedMap().toString();
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ get:
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentUser'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: debitorUuid
|
||||
- name: projectUuid
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: The UUID of the debitor, whose hosting assets are to be listed.
|
||||
description: The UUID of the project, whose hosting assets are to be listed.
|
||||
- name: parentAssetUuid
|
||||
in: query
|
||||
required: false
|
||||
|
@ -90,22 +90,10 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType("application/json")
|
||||
.body("", lenientlyEquals("""
|
||||
[
|
||||
{
|
||||
"type": "MANAGED_WEBSPACE",
|
||||
"identifier": "sec01",
|
||||
"caption": "some Webspace",
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"type": "MANAGED_WEBSPACE",
|
||||
"identifier": "fir01",
|
||||
"caption": "some Webspace",
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"type": "MANAGED_WEBSPACE",
|
||||
"identifier": "thi01",
|
||||
"caption": "some Webspace",
|
||||
"caption": "some Webspace",
|
||||
"config": {}
|
||||
}
|
||||
]
|
||||
|
@ -174,7 +174,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
||||
final var result = assetRepo.findAllByCriteria(null, null, MANAGED_WEBSPACE);
|
||||
|
||||
// then
|
||||
allTheseServersAreReturned(
|
||||
exactlyTheseAssetsAreReturned(
|
||||
result,
|
||||
"HsHostingAssetEntity(MANAGED_WEBSPACE, sec01, some Webspace, MANAGED_SERVER:vm1012, D-1000212:D-1000212 default project:separate ManagedWebspace)",
|
||||
"HsHostingAssetEntity(MANAGED_WEBSPACE, fir01, some Webspace, MANAGED_SERVER:vm1011, D-1000111:D-1000111 default project:separate ManagedWebspace)",
|
||||
@ -202,18 +202,19 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
||||
public void normalUser_canFilterAssetsRelatedToParentAsset() {
|
||||
// given
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var parentAssetUuid = assetRepo.findAllByCriteria(null, null, MANAGED_SERVER).stream()
|
||||
final var parentAssetUuid = assetRepo.findByIdentifier("vm1012").stream()
|
||||
.filter(ha -> ha.getType() == MANAGED_SERVER)
|
||||
.findAny().orElseThrow().getUuid();
|
||||
|
||||
// when
|
||||
context("superuser-alex@hostsharing.net", "hs_hosting_asset#vm1012:AGENT");
|
||||
final var result = assetRepo.findAllByCriteria(null, parentAssetUuid, null);
|
||||
|
||||
// then
|
||||
allTheseServersAreReturned(
|
||||
exactlyTheseAssetsAreReturned(
|
||||
result,
|
||||
"HsHostingAssetEntity(MANAGED_WEBSPACE, sec01, some Webspace, MANAGED_SERVER:vm1012, D-1000212:D-1000212 default project:separate ManagedWebspace)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@ -411,11 +412,4 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
||||
.extracting(input -> input.replaceAll("\"", ""))
|
||||
.containsExactlyInAnyOrder(serverNames);
|
||||
}
|
||||
|
||||
void allTheseServersAreReturned(final List<HsHostingAssetEntity> actualResult, final String... serverNames) {
|
||||
assertThat(actualResult)
|
||||
.extracting(HsHostingAssetEntity::toString)
|
||||
.contains(serverNames);
|
||||
actualResult.forEach(loadedEntity -> assertThat(loadedEntity.isLoaded()).isTrue());
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||
"{type=string[], propertyName=target, required=true, minLength=1}");
|
||||
"{type=string[], propertyName=target, elementProperty={type=string, propertyName=target, matchesRegEx=[^[a-z]{3}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_.±]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$], maxLength=320}, required=true, minLength=1}");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -77,7 +77,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'EMAIL_ALIAS:xyz00-office.parentAsset' must be of type MANAGED_WEBSPACE but is of type MANAGED_SERVER");
|
||||
"'EMAIL_ALIAS:xyz00-office.config.target' is expected to match any of [^[a-z]{3}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_.±]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$] but 'garbage' does not match any");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -88,7 +88,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
|
||||
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
||||
.identifier("abc00-office")
|
||||
.config(Map.ofEntries(
|
||||
entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com"))
|
||||
entry("target", Array.of("office@example.com"))
|
||||
))
|
||||
.build();
|
||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType());
|
||||
@ -108,9 +108,10 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
|
||||
.type(EMAIL_ALIAS)
|
||||
.bookingItem(TEST_MANAGED_SERVER_BOOKING_ITEM)
|
||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
||||
.assignedToAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
||||
.identifier("abc00-office")
|
||||
.config(Map.ofEntries(
|
||||
entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com"))
|
||||
entry("target", Array.of("office@example.com"))
|
||||
))
|
||||
.build();
|
||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType());
|
||||
@ -120,6 +121,8 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'identifier' expected to match '^xyz00$|^xyz00-[a-z0-9]+$', but is 'abc00-office'");
|
||||
"'EMAIL_ALIAS:abc00-office.bookingItem' must be null but is set to D-1234500:test project:test project booking item",
|
||||
"'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");
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ class HsHostingAssetEntityValidatorRegistryUnitTest {
|
||||
HsHostingAssetType.CLOUD_SERVER,
|
||||
HsHostingAssetType.MANAGED_SERVER,
|
||||
HsHostingAssetType.MANAGED_WEBSPACE,
|
||||
HsHostingAssetType.UNIX_USER
|
||||
HsHostingAssetType.UNIX_USER,
|
||||
HsHostingAssetType.EMAIL_ALIAS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
|
||||
"'UNIX_USER:abc00-temp.config.HDD soft quota' is expected to be at most 100 but is 200",
|
||||
"'UNIX_USER:abc00-temp.config.shell' is expected to be one of [/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd] but is '/is/invalid'",
|
||||
"'UNIX_USER:abc00-temp.config.homedir' is readonly but given as '/is/read-only'",
|
||||
"'UNIX_USER:abc00-temp.config.totpKey' is expected to be match ^0x([0-9A-Fa-f]{2})+$ but provided value does not match",
|
||||
"'UNIX_USER:abc00-temp.config.totpKey' is expected to match any of [^0x([0-9A-Fa-f]{2})+$] but provided value does not match any",
|
||||
"'UNIX_USER:abc00-temp.config.password' length is expected to be at min 8 but length of provided value is 5",
|
||||
"'UNIX_USER:abc00-temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"
|
||||
);
|
||||
@ -168,7 +168,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
|
||||
"{type=integer, propertyName=HDD soft quota, unit=GB, maxFrom=HDD hard quota}",
|
||||
"{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}",
|
||||
"{type=string, propertyName=homedir, readOnly=true, computed=true}",
|
||||
"{type=string, propertyName=totpKey, matchesRegEx=^0x([0-9A-Fa-f]{2})+$, minLength=20, maxLength=256, writeOnly=true, undisclosed=true}",
|
||||
"{type=string, propertyName=totpKey, matchesRegEx=[^0x([0-9A-Fa-f]{2})+$], minLength=20, maxLength=256, writeOnly=true, undisclosed=true}",
|
||||
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}"
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user