Compare commits
2 Commits
d504347ac2
...
c59fb34b8b
Author | SHA1 | Date | |
---|---|---|---|
|
c59fb34b8b | ||
|
94d96548d4 |
@ -29,7 +29,7 @@ class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
.values("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd")
|
||||
.withDefault("/bin/false"),
|
||||
stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir),
|
||||
stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).hidden().writeOnly().optional(),
|
||||
stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(),
|
||||
passwordProperty("password").minLength(8).maxLength(40).writeOnly());
|
||||
}
|
||||
|
||||
|
@ -32,20 +32,20 @@ public class EnumerationProperty extends ValidatableProperty<String> {
|
||||
}
|
||||
|
||||
public void deferredInit(final ValidatableProperty<?>[] allProperties) {
|
||||
if (deferredInit != null) {
|
||||
if (hasDeferredInit()) {
|
||||
if (this.values != null) {
|
||||
throw new IllegalStateException("property " + this + " already has values");
|
||||
}
|
||||
this.values = deferredInit.apply(allProperties);
|
||||
this.values = doDeferredInit(allProperties);
|
||||
}
|
||||
}
|
||||
|
||||
public ValidatableProperty<String> valuesFromProperties(final String propertyNamePrefix) {
|
||||
this.deferredInit = (ValidatableProperty<?>[] allProperties) -> stream(allProperties)
|
||||
this.setDeferredInit( (ValidatableProperty<?>[] allProperties) -> stream(allProperties)
|
||||
.map(ValidatableProperty::propertyName)
|
||||
.filter(name -> name.startsWith(propertyNamePrefix))
|
||||
.map(name -> name.substring(propertyNamePrefix.length()))
|
||||
.toArray(String[]::new);
|
||||
.toArray(String[]::new));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
|
||||
public Map<String, Object> postProcess(final E entity, final Map<String, Object> config) {
|
||||
final var copy = new HashMap<>(config);
|
||||
stream(propertyValidators).forEach(p -> {
|
||||
if ( p.writeOnly) {
|
||||
if ( p.isWriteOnly()) {
|
||||
copy.remove(p.propertyName);
|
||||
}
|
||||
if (p.isComputed()) {
|
||||
|
@ -10,7 +10,7 @@ public class PasswordProperty extends StringProperty {
|
||||
|
||||
private PasswordProperty(final String propertyName) {
|
||||
super(propertyName);
|
||||
hidden();
|
||||
undisclosed();
|
||||
}
|
||||
|
||||
public static PasswordProperty passwordProperty(final String propertyName) {
|
||||
|
@ -6,7 +6,6 @@ import net.hostsharing.hsadminng.mapper.Array;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@Setter
|
||||
public class StringProperty extends ValidatableProperty<String> {
|
||||
|
||||
@ -14,11 +13,11 @@ public class StringProperty extends ValidatableProperty<String> {
|
||||
ValidatableProperty.KEY_ORDER_HEAD,
|
||||
Array.of("matchesRegEx", "minLength", "maxLength"),
|
||||
ValidatableProperty.KEY_ORDER_TAIL,
|
||||
Array.of("hidden"));
|
||||
Array.of("undisclosed"));
|
||||
private Pattern matchesRegEx;
|
||||
private Integer minLength;
|
||||
private Integer maxLength;
|
||||
private boolean hidden;
|
||||
private boolean undisclosed;
|
||||
|
||||
protected StringProperty(final String propertyName) {
|
||||
super(String.class, propertyName, KEY_ORDER);
|
||||
@ -43,8 +42,13 @@ public class StringProperty extends ValidatableProperty<String> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public StringProperty hidden() {
|
||||
this.hidden = true;
|
||||
/**
|
||||
* The property value is not disclosed in error messages.
|
||||
*
|
||||
* @return this;
|
||||
*/
|
||||
public StringProperty undisclosed() {
|
||||
this.undisclosed = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -59,13 +63,13 @@ public class StringProperty extends ValidatableProperty<String> {
|
||||
if (matchesRegEx != null && !matchesRegEx.matcher(propValue).matches()) {
|
||||
result.add(propertyName + "' is expected to be match " + matchesRegEx + " but " + display(propValue) + " does not match");
|
||||
}
|
||||
if (readOnly && propValue != null) {
|
||||
if (isReadOnly() && propValue != null) {
|
||||
result.add(propertyName + "' is readonly but given as " + display(propValue));
|
||||
}
|
||||
}
|
||||
|
||||
private String display(final String propValue) {
|
||||
return hidden ? "provided value" : ("'" + propValue + "'");
|
||||
return undisclosed ? "provided value" : ("'" + propValue + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,8 @@
|
||||
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 net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||
@ -21,6 +23,7 @@ import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public abstract class ValidatableProperty<T> {
|
||||
|
||||
@ -29,16 +32,28 @@ public abstract class ValidatableProperty<T> {
|
||||
|
||||
final Class<T> type;
|
||||
final String propertyName;
|
||||
|
||||
@JsonIgnore
|
||||
private final String[] keyOrder;
|
||||
|
||||
private Boolean required;
|
||||
private T defaultValue;
|
||||
protected Function<PropertiesProvider, T> computedBy;
|
||||
protected boolean computed; // used in descriptor, because computedBy cannot be rendered to a text string
|
||||
protected boolean readOnly;
|
||||
protected boolean writeOnly;
|
||||
|
||||
protected Function<ValidatableProperty<?>[], T[]> deferredInit;
|
||||
@JsonIgnore
|
||||
private Function<PropertiesProvider, T> computedBy;
|
||||
|
||||
@Accessors(makeFinal = true, chain = true, fluent = false)
|
||||
private boolean computed; // used in descriptor, because computedBy cannot be rendered to a text string
|
||||
|
||||
@Accessors(makeFinal = true, chain = true, fluent = false)
|
||||
private boolean readOnly;
|
||||
|
||||
@Accessors(makeFinal = true, chain = true, fluent = false)
|
||||
private boolean writeOnly;
|
||||
|
||||
private Function<ValidatableProperty<?>[], T[]> deferredInit;
|
||||
private boolean isTotalsValidator = false;
|
||||
|
||||
@JsonIgnore
|
||||
private List<Function<HsBookingItemEntity, List<String>>> asTotalLimitValidators; // TODO.impl: move to BookingItemIntegerProperty
|
||||
|
||||
@ -48,6 +63,17 @@ public abstract class ValidatableProperty<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void setDeferredInit(final Function<ValidatableProperty<?>[], T[]> function) {
|
||||
this.deferredInit = function;
|
||||
}
|
||||
|
||||
public boolean hasDeferredInit() {
|
||||
return deferredInit != null;
|
||||
}
|
||||
|
||||
public T[] doDeferredInit(final ValidatableProperty<?>[] allProperties) {
|
||||
return deferredInit.apply(allProperties);
|
||||
}
|
||||
|
||||
public ValidatableProperty<T> writeOnly() {
|
||||
this.writeOnly = true;
|
||||
@ -61,7 +87,6 @@ public abstract class ValidatableProperty<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ValidatableProperty<T> required() {
|
||||
required = TRUE;
|
||||
return this;
|
||||
@ -231,10 +256,6 @@ public abstract class ValidatableProperty<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isComputed() {
|
||||
return computedBy != null;
|
||||
}
|
||||
|
||||
public <E extends PropertiesProvider> T compute(final E entity) {
|
||||
return computedBy.apply(entity);
|
||||
}
|
||||
|
@ -124,8 +124,8 @@ 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, hidden=true}",
|
||||
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, hidden=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, undisclosed=true}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +54,6 @@ public class JsonMatcher extends BaseMatcher<CharSequence> {
|
||||
return true;
|
||||
} catch (final JSONException | JsonProcessingException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (final Exception e ) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,5 +61,4 @@ public class JsonMatcher extends BaseMatcher<CharSequence> {
|
||||
public void describeTo(final Description description) {
|
||||
description.appendText("leniently matches JSON");
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user