add-unix-user-hosting-asset-validation #66
@ -47,7 +47,7 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
|
|||||||
@Override
|
@Override
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||||
return sequentiallyValidate(
|
return sequentiallyValidate(
|
||||||
() -> validateEntityReferences(assetEntity),
|
() -> validateEntityReferencesAndProperties(assetEntity),
|
||||||
() -> validateIdentifierPattern(assetEntity), // might need proper parentAsset or billingItem
|
() -> validateIdentifierPattern(assetEntity), // might need proper parentAsset or billingItem
|
||||||
() -> optionallyValidate(assetEntity.getBookingItem()),
|
() -> optionallyValidate(assetEntity.getBookingItem()),
|
||||||
() -> optionallyValidate(assetEntity.getParentAsset()),
|
() -> optionallyValidate(assetEntity.getParentAsset()),
|
||||||
@ -55,7 +55,7 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> validateEntityReferences(final HsHostingAssetEntity assetEntity) {
|
private List<String> validateEntityReferencesAndProperties(final HsHostingAssetEntity assetEntity) {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate),
|
validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate),
|
||||||
validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate),
|
validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate),
|
||||||
|
@ -5,14 +5,28 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
|||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
||||||
|
|
||||||
class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
|
class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
|
|
||||||
HsUnixUserHostingAssetValidator() {
|
HsUnixUserHostingAssetValidator() {
|
||||||
super( BookingItem.mustBeNull(),
|
super( BookingItem.mustBeNull(),
|
||||||
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
|
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
|
||||||
AssignedToAsset.mustBeNull(),
|
AssignedToAsset.mustBeNull(),
|
||||||
AlarmContact.isOptional(), // TODO.spec: for quota notifications
|
AlarmContact.isOptional(),
|
||||||
NO_EXTRA_PROPERTIES); // TODO.spec: yet to be specified
|
|
||||||
|
integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(),
|
||||||
|
integerProperty("SSD soft quota").unit("GB").minFrom("SSD hard quota").optional(),
|
||||||
|
integerProperty("HDD hard quota").unit("GB").maxFrom("HDD").optional(),
|
||||||
|
integerProperty("HDD soft quota").unit("GB").minFrom("HDD hard quota").optional(),
|
||||||
|
enumerationProperty("shell")
|
||||||
|
.values("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd")
|
||||||
|
.withDefault("/bin/false"),
|
||||||
|
stringProperty("homedir").readOnly(),
|
||||||
|
stringProperty("totpKey").matchesRegEx("^0x\\([0-9A-Fa-f][0-9A-Fa-f]\\)+$").minLength(12).maxLength(32).writeOnly().optional(),
|
||||||
|
stringProperty("password").minLength(8).maxLength(40).writeOnly()); // FIXME: spec
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,7 +35,7 @@ public class EnumerationProperty extends ValidatableProperty<String> {
|
|||||||
public void deferredInit(final ValidatableProperty<?>[] allProperties) {
|
public void deferredInit(final ValidatableProperty<?>[] allProperties) {
|
||||||
if (deferredInit != null) {
|
if (deferredInit != null) {
|
||||||
if (this.values != null) {
|
if (this.values != null) {
|
||||||
throw new IllegalStateException("property " + toString() + " already has values");
|
throw new IllegalStateException("property " + this + " already has values");
|
||||||
}
|
}
|
||||||
this.values = deferredInit.apply(allProperties);
|
this.values = deferredInit.apply(allProperties);
|
||||||
}
|
}
|
||||||
|
@ -40,14 +40,19 @@ public abstract class HsEntityValidator<E> {
|
|||||||
|
|
||||||
protected ArrayList<String> validateProperties(final Map<String, Object> properties) {
|
protected ArrayList<String> validateProperties(final Map<String, Object> properties) {
|
||||||
final var result = new ArrayList<String>();
|
final var result = new ArrayList<String>();
|
||||||
|
|
||||||
|
// verify that all actually given properties are specified
|
||||||
properties.keySet().forEach( givenPropName -> {
|
properties.keySet().forEach( givenPropName -> {
|
||||||
if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) {
|
if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) {
|
||||||
result.add(givenPropName + "' is not expected but is set to '" + properties.get(givenPropName) + "'");
|
result.add(givenPropName + "' is not expected but is set to '" + properties.get(givenPropName) + "'");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// run all property validators
|
||||||
stream(propertyValidators).forEach(pv -> {
|
stream(propertyValidators).forEach(pv -> {
|
||||||
result.addAll(pv.validate(properties));
|
result.addAll(pv.validate(properties));
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,9 @@ public class IntegerProperty extends ValidatableProperty<Integer> {
|
|||||||
|
|
||||||
private String unit;
|
private String unit;
|
||||||
private Integer min;
|
private Integer min;
|
||||||
|
private String minFrom;
|
||||||
private Integer max;
|
private Integer max;
|
||||||
|
private String maxFrom;
|
||||||
private Integer step;
|
private Integer step;
|
||||||
|
|
||||||
public static IntegerProperty integerProperty(final String propertyName) {
|
public static IntegerProperty integerProperty(final String propertyName) {
|
||||||
@ -27,6 +29,16 @@ public class IntegerProperty extends ValidatableProperty<Integer> {
|
|||||||
super(Integer.class, propertyName, KEY_ORDER);
|
super(Integer.class, propertyName, KEY_ORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntegerProperty minFrom(final String propertyName) {
|
||||||
|
minFrom = propertyName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerProperty maxFrom(final String propertyName) {
|
||||||
|
maxFrom = propertyName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String unit() {
|
public String unit() {
|
||||||
return unit;
|
return unit;
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.hostsharing.hsadminng.mapper.Array;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public class StringProperty extends ValidatableProperty<String> {
|
||||||
|
|
||||||
|
private static final String[] KEY_ORDER = Array.join(
|
||||||
|
ValidatableProperty.KEY_ORDER_HEAD,
|
||||||
|
Array.of("values"),
|
||||||
|
ValidatableProperty.KEY_ORDER_TAIL);
|
||||||
|
private Pattern regExPattern;
|
||||||
|
private Integer minLength;
|
||||||
|
private Integer maxLength;
|
||||||
|
private boolean writeOnly;
|
||||||
|
private boolean readOnly;
|
||||||
|
|
||||||
|
private StringProperty(final String propertyName) {
|
||||||
|
super(String.class, propertyName, KEY_ORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringProperty stringProperty(final String propertyName) {
|
||||||
|
return new StringProperty(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty minLength(final int minLength) {
|
||||||
|
this.minLength = minLength;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty maxLength(final int maxLength) {
|
||||||
|
this.maxLength = maxLength;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty matchesRegEx(final String regExPattern) {
|
||||||
|
this.regExPattern = Pattern.compile(regExPattern);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
|||||||
|
public StringProperty writeOnly() {
|
||||||
|
this.writeOnly = true;
|
||||||
|
super.optional();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty readOnly() {
|
||||||
|
this.readOnly = true;
|
||||||
|
super.optional();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validate(final ArrayList<String> result, final String propValue, final Map<String, Object> props) {
|
||||||
|
if (minLength != null && propValue.length()<minLength) {
|
||||||
|
result.add(propertyName + "' length is expected to be at min " + minLength + " but length of '" + propValue+ "' is " + propValue.length());
|
||||||
|
}
|
||||||
|
if (maxLength != null && propValue.length()>maxLength) {
|
||||||
|
result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of '" + propValue+ "' is " + propValue.length());
|
||||||
|
}
|
||||||
|
if (regExPattern != null && !regExPattern.matcher(propValue).matches()) {
|
||||||
|
result.add(propertyName + "' is expected to be match " + regExPattern + " but '" + propValue+ "' does not match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String simpleTypeName() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
}
|
@ -12,21 +12,35 @@ import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.
|
|||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class TestHsBookingItem {
|
public class TestHsBookingItem {
|
||||||
|
|
||||||
public static final HsBookingItemEntity TEST_MANAGED_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
|
||||||
.project(TEST_PROJECT)
|
|
||||||
.type(HsBookingItemType.MANAGED_SERVER)
|
|
||||||
.caption("test project booking item")
|
|
||||||
.resources(Map.ofEntries(
|
|
||||||
entry("someThing", 1),
|
|
||||||
entry("anotherThing", "blue")
|
|
||||||
))
|
|
||||||
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static final HsBookingItemEntity TEST_CLOUD_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
public static final HsBookingItemEntity TEST_CLOUD_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
.project(TEST_PROJECT)
|
.project(TEST_PROJECT)
|
||||||
.type(HsBookingItemType.CLOUD_SERVER)
|
.type(HsBookingItemType.CLOUD_SERVER)
|
||||||
.caption("test cloud server booking item")
|
.caption("test cloud server booking item")
|
||||||
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static final HsBookingItemEntity TEST_MANAGED_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
|
.project(TEST_PROJECT)
|
||||||
|
.type(HsBookingItemType.MANAGED_SERVER)
|
||||||
|
.caption("test project booking item")
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("CPUs", 2),
|
||||||
|
entry("RAM", 4),
|
||||||
|
entry("SSD", 50),
|
||||||
|
entry("Traffic", 250)
|
||||||
|
))
|
||||||
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public static final HsBookingItemEntity TEST_MANAGED_WEBSPACE_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
|
.parentItem(TEST_MANAGED_SERVER_BOOKING_ITEM)
|
||||||
|
.type(HsBookingItemType.MANAGED_WEBSPACE)
|
||||||
|
.caption("test managed webspace item")
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("SSD", 25),
|
||||||
|
entry("Traffic", 250)
|
||||||
|
))
|
||||||
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
|
.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,78 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_SERVER_BOOKING_ITEM;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_WEBSPACE_BOOKING_ITEM;
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
|
||||||
|
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class HsUnixUserHostingAssetValidatorUnitTest {
|
class HsUnixUserHostingAssetValidatorUnitTest {
|
||||||
|
|
||||||
|
private final HsHostingAssetEntity TEST_MANAGED_SERVER_HOSTING_ASSET = HsHostingAssetEntity.builder()
|
||||||
|
.type(HsHostingAssetType.MANAGED_SERVER)
|
||||||
|
.identifier("vm1234")
|
||||||
|
.caption("some managed server")
|
||||||
|
.bookingItem(TEST_MANAGED_SERVER_BOOKING_ITEM)
|
||||||
|
.build();
|
||||||
|
private HsHostingAssetEntity TEST_MANAGED_WEBSPACE_HOSTING_ASSET = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(TEST_MANAGED_WEBSPACE_BOOKING_ITEM)
|
||||||
|
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
||||||
|
.identifier("abc00")
|
||||||
|
.build();;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesValidUnixUser() {
|
||||||
|
// given
|
||||||
|
final var unixUserHostingAsset = HsHostingAssetEntity.builder()
|
||||||
|
.type(UNIX_USER)
|
||||||
|
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
||||||
|
.identifier("abc00-temp")
|
||||||
|
.caption("some valid test UnixUser")
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(unixUserHostingAsset);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesUnixUserProperties() {
|
||||||
|
// given
|
||||||
|
final var unixUserHostingAsset = HsHostingAssetEntity.builder()
|
||||||
|
.type(UNIX_USER)
|
||||||
|
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
||||||
|
.identifier("abc00-temp")
|
||||||
|
.caption("some test UnixUser with invalid properties")
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("SSD hard quota", 1000),
|
||||||
|
entry("SSD soft quota", 2000),
|
||||||
|
entry("HDD hard quota", 1000),
|
||||||
|
entry("HDD soft quota", 2000),
|
||||||
|
entry("homedir", "/is/read-only"),
|
||||||
|
entry("totpKey", "should be a hex number"),
|
||||||
|
entry("password", "should be a hex number")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(unixUserHostingAsset);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void validatesInvalidIdentifier() {
|
void validatesInvalidIdentifier() {
|
||||||
// given
|
// given
|
||||||
@ -19,7 +83,6 @@ class HsUnixUserHostingAssetValidatorUnitTest {
|
|||||||
.build();
|
.build();
|
||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
hsh-michaelhoennig marked this conversation as resolved
hsh-marcsandlus
commented
evtl. nur false, bash, csh, ... evtl. nur false, bash, csh, ...
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = validator.validate(unixUserHostingAsset);
|
final var result = validator.validate(unixUserHostingAsset);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user
doku