Compare commits

...

3 Commits

Author SHA1 Message Date
Michael Hoennig
89808cecf7 use HsHostingAssetRawEntity for ImportHostingAssets 2024-07-31 12:20:50 +02:00
Michael Hoennig
6c49ba2478 unixuser+emailaliases import generally working with test data 2024-07-31 05:44:40 +02:00
Michael Hoennig
7193772f98 allow _ in unixuser names etc. 2024-07-30 10:02:01 +02:00
44 changed files with 616 additions and 293 deletions

View File

@ -32,6 +32,7 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne; import jakarta.persistence.OneToOne;
import jakarta.persistence.PostLoad;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.persistence.Transient; import jakarta.persistence.Transient;
import jakarta.persistence.Version; import jakarta.persistence.Version;
@ -124,6 +125,14 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject<HsBookingI
@Transient @Transient
private PatchableMapWrapper<Object> resourcesWrapper; private PatchableMapWrapper<Object> resourcesWrapper;
@Transient
private boolean isLoaded;
@PostLoad
public void markAsLoaded() {
this.isLoaded = true;
}
public PatchableMapWrapper<Object> getResources() { public PatchableMapWrapper<Object> getResources() {
return PatchableMapWrapper.of(resourcesWrapper, (newWrapper) -> {resourcesWrapper = newWrapper; }, resources ); return PatchableMapWrapper.of(resourcesWrapper, (newWrapper) -> {resourcesWrapper = newWrapper; }, resources );
} }

View File

@ -0,0 +1,69 @@
package net.hostsharing.hsadminng.hs.hosting.asset;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static java.util.Collections.emptyMap;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public interface HsHostingAsset extends Stringifyable, RbacObject<HsHostingAsset>, PropertiesProvider {
Stringify<HsHostingAsset> stringify = stringify(HsHostingAsset.class)
.withProp(HsHostingAsset::getType)
.withProp(HsHostingAsset::getIdentifier)
.withProp(HsHostingAsset::getCaption)
.withProp(HsHostingAsset::getParentAsset)
.withProp(HsHostingAsset::getAssignedToAsset)
.withProp(HsHostingAsset::getBookingItem)
.withProp(HsHostingAsset::getConfig)
.quotedValues(false);
HsHostingAssetType getType();
HsHostingAsset getParentAsset();
void setIdentifier(String s);
String getIdentifier();
HsBookingItemEntity getBookingItem();
HsHostingAsset getAssignedToAsset();
HsOfficeContactEntity getAlarmContact();
List<? extends HsHostingAsset> getSubHostingAssets();
String getCaption();
Map<String, Object> getConfig();
default HsBookingProjectEntity getRelatedProject() {
return Optional.ofNullable(getBookingItem())
.map(HsBookingItemEntity::getRelatedProject)
.orElseGet(() -> Optional.ofNullable(getParentAsset())
.map(HsHostingAsset::getRelatedProject)
.orElse(null));
}
@Override
default Object getContextValue(final String propName) {
final var v = directProps().get(propName);
if (v != null) {
return v;
}
if (getBookingItem() != null) {
return getBookingItem().getResources().get(propName);
}
if (getParentAsset() != null && getParentAsset().getBookingItem() != null) {
return getParentAsset().getBookingItem().getResources().get(propName);
}
return emptyMap();
}
@Override
default String toShortString() {
return getType() + ":" + getIdentifier();
}
}

View File

@ -8,15 +8,10 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
@ -39,10 +34,8 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static java.util.Collections.emptyMap;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
@ -70,17 +63,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class HsHostingAssetEntity implements Stringifyable, RbacObject<HsHostingAssetEntity>, PropertiesProvider { public class HsHostingAssetEntity implements HsHostingAsset {
private static Stringify<HsHostingAssetEntity> stringify = stringify(HsHostingAssetEntity.class)
.withProp(HsHostingAssetEntity::getType)
.withProp(HsHostingAssetEntity::getIdentifier)
.withProp(HsHostingAssetEntity::getCaption)
.withProp(HsHostingAssetEntity::getParentAsset)
.withProp(HsHostingAssetEntity::getAssignedToAsset)
.withProp(HsHostingAssetEntity::getBookingItem)
.withProp(HsHostingAssetEntity::getConfig)
.quotedValues(false);
@Id @Id
@GeneratedValue @GeneratedValue
@ -136,14 +119,6 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject<HsHosting
this.isLoaded = true; this.isLoaded = true;
} }
public HsBookingProjectEntity getRelatedProject() {
return Optional.ofNullable(bookingItem)
.map(HsBookingItemEntity::getRelatedProject)
.orElseGet(() -> Optional.ofNullable(parentAsset)
.map(HsHostingAssetEntity::getRelatedProject)
.orElse(null));
}
public PatchableMapWrapper<Object> getConfig() { public PatchableMapWrapper<Object> getConfig() {
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config);
} }
@ -157,30 +132,9 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject<HsHosting
return config; return config;
} }
@Override
public Object getContextValue(final String propName) {
final var v = config.get(propName);
if (v != null) {
return v;
}
if (bookingItem != null) {
return bookingItem.getResources().get(propName);
}
if (parentAsset != null && parentAsset.getBookingItem() != null) {
return parentAsset.getBookingItem().getResources().get(propName);
}
return emptyMap();
}
@Override @Override
public String toString() { public String toString() {
return stringify.apply(this); return stringify.using(HsHostingAssetEntity.class).apply(this);
}
@Override
public String toShortString() {
return type + ":" + identifier;
} }
public static RbacView rbac() { public static RbacView rbac() {

View File

@ -39,7 +39,7 @@ public interface HsHostingAssetRepository extends Repository<HsHostingAssetEntit
return findAllByCriteriaImpl(projectUuid, parentAssetUuid, HsHostingAssetType.asString(type)); return findAllByCriteriaImpl(projectUuid, parentAssetUuid, HsHostingAssetType.asString(type));
} }
HsHostingAssetEntity save(HsHostingAssetEntity current); HsHostingAssetEntity save(HsHostingAsset current);
int deleteByUuid(final UUID uuid); int deleteByUuid(final UUID uuid);

View File

@ -384,29 +384,29 @@ class EntityTypeRelation<E, T extends Node> {
" *==> "); " *==> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionalParent(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAsset, HsHostingAssetType> optionalParent(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>( return new EntityTypeRelation<>(
OPTIONAL, OPTIONAL,
PARENT_ASSET, PARENT_ASSET,
HsHostingAssetEntity::getParentAsset, HsHostingAsset::getParentAsset,
hostingAssetType, hostingAssetType,
" o..> "); " o..> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAsset, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>( return new EntityTypeRelation<>(
REQUIRED, REQUIRED,
PARENT_ASSET, PARENT_ASSET,
HsHostingAssetEntity::getParentAsset, HsHostingAsset::getParentAsset,
hostingAssetType, hostingAssetType,
" *==> "); " *==> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> assignedTo(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAsset, HsHostingAssetType> assignedTo(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>( return new EntityTypeRelation<>(
REQUIRED, REQUIRED,
ASSIGNED_TO_ASSET, ASSIGNED_TO_ASSET,
HsHostingAssetEntity::getAssignedToAsset, HsHostingAsset::getAssignedToAsset,
hostingAssetType, hostingAssetType,
" o--> "); " o--> ");
} }
@ -416,11 +416,11 @@ class EntityTypeRelation<E, T extends Node> {
return this; return this;
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAsset, HsHostingAssetType> optionallyAssignedTo(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>( return new EntityTypeRelation<>(
OPTIONAL, OPTIONAL,
ASSIGNED_TO_ASSET, ASSIGNED_TO_ASSET,
HsHostingAssetEntity::getAssignedToAsset, HsHostingAsset::getAssignedToAsset,
hostingAssetType, hostingAssetType,
" o..> "); " o..> ");
} }

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.errors.MultiValidationException; import net.hostsharing.hsadminng.errors.MultiValidationException;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
@ -10,17 +10,17 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
/** /**
* Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsHostingAssetEntity into a readable API. * Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsHostingAsset into a readable API.
*/ */
public class HostingAssetEntitySaveProcessor { public class HostingAssetEntitySaveProcessor {
private final HsEntityValidator<HsHostingAssetEntity> validator; private final HsEntityValidator<HsHostingAsset> validator;
private String expectedStep = "preprocessEntity"; private String expectedStep = "preprocessEntity";
private final EntityManager em; private final EntityManager em;
private HsHostingAssetEntity entity; private HsHostingAsset entity;
private HsHostingAssetResource resource; private HsHostingAssetResource resource;
public HostingAssetEntitySaveProcessor(final EntityManager em, final HsHostingAssetEntity entity) { public HostingAssetEntitySaveProcessor(final EntityManager em, final HsHostingAsset entity) {
this.em = em; this.em = em;
this.entity = entity; this.entity = entity;
this.validator = HostingAssetEntityValidatorRegistry.forType(entity.getType()); this.validator = HostingAssetEntityValidatorRegistry.forType(entity.getType());
@ -40,6 +40,17 @@ public class HostingAssetEntitySaveProcessor {
return this; return this;
} }
/// validates the entity itself including its properties, but ignoring some error messages for import of legacy data
public HostingAssetEntitySaveProcessor validateEntityIgnoring(final String ignoreRegExp) {
step("validateEntity", "prepareForSave");
MultiValidationException.throwIfNotEmpty(
validator.validateEntity(entity).stream()
.filter(errorMsg -> !errorMsg.matches(ignoreRegExp))
.toList()
);
return this;
}
/// hashing passwords etc. /// hashing passwords etc.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public HostingAssetEntitySaveProcessor prepareForSave() { public HostingAssetEntitySaveProcessor prepareForSave() {
@ -48,7 +59,7 @@ public class HostingAssetEntitySaveProcessor {
return this; return this;
} }
public HostingAssetEntitySaveProcessor saveUsing(final Function<HsHostingAssetEntity, HsHostingAssetEntity> saveFunction) { public HostingAssetEntitySaveProcessor saveUsing(final Function<HsHostingAsset, HsHostingAsset> saveFunction) {
step("saveUsing", "validateContext"); step("saveUsing", "validateContext");
entity = saveFunction.apply(entity); entity = saveFunction.apply(entity);
return this; return this;
@ -63,7 +74,7 @@ public class HostingAssetEntitySaveProcessor {
/// maps entity to JSON resource representation /// maps entity to JSON resource representation
public HostingAssetEntitySaveProcessor mapUsing( public HostingAssetEntitySaveProcessor mapUsing(
final Function<HsHostingAssetEntity, HsHostingAssetResource> mapFunction) { final Function<HsHostingAsset, HsHostingAssetResource> mapFunction) {
step("mapUsing", "revampProperties"); step("mapUsing", "revampProperties");
resource = mapFunction.apply(entity); resource = mapFunction.apply(entity);
return this; return this;

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; 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.validators.HsBookingItemEntityValidatorRegistry; import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
@ -23,13 +23,13 @@ import static java.util.Arrays.stream;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> { public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHostingAsset> {
static final ValidatableProperty<?, ?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?, ?>[0]; static final ValidatableProperty<?, ?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?, ?>[0];
private final ReferenceValidator<HsBookingItemEntity, HsBookingItemType> bookingItemReferenceValidation; private final ReferenceValidator<HsBookingItemEntity, HsBookingItemType> bookingItemReferenceValidation;
private final ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> parentAssetReferenceValidation; private final ReferenceValidator<HsHostingAsset, HsHostingAssetType> parentAssetReferenceValidation;
private final ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> assignedToAssetReferenceValidation; private final ReferenceValidator<HsHostingAsset, HsHostingAssetType> assignedToAssetReferenceValidation;
private final HostingAssetEntityValidator.AlarmContact alarmContactValidation; private final HostingAssetEntityValidator.AlarmContact alarmContactValidation;
HostingAssetEntityValidator( HostingAssetEntityValidator(
@ -40,23 +40,23 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
this.bookingItemReferenceValidation = new ReferenceValidator<>( this.bookingItemReferenceValidation = new ReferenceValidator<>(
assetType.bookingItemPolicy(), assetType.bookingItemPolicy(),
assetType.bookingItemTypes(), assetType.bookingItemTypes(),
HsHostingAssetEntity::getBookingItem, HsHostingAsset::getBookingItem,
HsBookingItemEntity::getType); HsBookingItemEntity::getType);
this.parentAssetReferenceValidation = new ReferenceValidator<>( this.parentAssetReferenceValidation = new ReferenceValidator<>(
assetType.parentAssetPolicy(), assetType.parentAssetPolicy(),
assetType.parentAssetTypes(), assetType.parentAssetTypes(),
HsHostingAssetEntity::getParentAsset, HsHostingAsset::getParentAsset,
HsHostingAssetEntity::getType); HsHostingAsset::getType);
this.assignedToAssetReferenceValidation = new ReferenceValidator<>( this.assignedToAssetReferenceValidation = new ReferenceValidator<>(
assetType.assignedToAssetPolicy(), assetType.assignedToAssetPolicy(),
assetType.assignedToAssetTypes(), assetType.assignedToAssetTypes(),
HsHostingAssetEntity::getAssignedToAsset, HsHostingAsset::getAssignedToAsset,
HsHostingAssetEntity::getType); HsHostingAsset::getType);
this.alarmContactValidation = alarmContactValidation; this.alarmContactValidation = alarmContactValidation;
} }
@Override @Override
public List<String> validateEntity(final HsHostingAssetEntity assetEntity) { public List<String> validateEntity(final HsHostingAsset assetEntity) {
return sequentiallyValidate( return sequentiallyValidate(
() -> validateEntityReferencesAndProperties(assetEntity), () -> validateEntityReferencesAndProperties(assetEntity),
() -> validateIdentifierPattern(assetEntity) () -> validateIdentifierPattern(assetEntity)
@ -64,7 +64,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
} }
@Override @Override
public List<String> validateContext(final HsHostingAssetEntity assetEntity) { public List<String> validateContext(final HsHostingAsset assetEntity) {
return sequentiallyValidate( return sequentiallyValidate(
() -> optionallyValidate(assetEntity.getBookingItem()), () -> optionallyValidate(assetEntity.getBookingItem()),
() -> optionallyValidate(assetEntity.getParentAsset()), () -> optionallyValidate(assetEntity.getParentAsset()),
@ -72,7 +72,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
); );
} }
private List<String> validateEntityReferencesAndProperties(final HsHostingAssetEntity assetEntity) { private List<String> validateEntityReferencesAndProperties(final HsHostingAsset assetEntity) {
return Stream.of( return Stream.of(
validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate), validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate),
validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate), validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate),
@ -86,17 +86,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
} }
private List<String> validateReferencedEntity( private List<String> validateReferencedEntity(
final HsHostingAssetEntity assetEntity, final HsHostingAsset assetEntity,
final String referenceFieldName, final String referenceFieldName,
final BiFunction<HsHostingAssetEntity, String, List<String>> validator) { final BiFunction<HsHostingAsset, String, List<String>> validator) {
return enrich(prefix(assetEntity.toShortString()), validator.apply(assetEntity, referenceFieldName)); return enrich(prefix(assetEntity.toShortString()), validator.apply(assetEntity, referenceFieldName));
} }
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) { private List<String> validateProperties(final HsHostingAsset assetEntity) {
return enrich(prefix(assetEntity.toShortString(), "config"), super.validateProperties(assetEntity)); return enrich(prefix(assetEntity.toShortString(), "config"), super.validateProperties(assetEntity));
} }
private static List<String> optionallyValidate(final HsHostingAssetEntity assetEntity) { private static List<String> optionallyValidate(final HsHostingAsset assetEntity) {
return assetEntity != null return assetEntity != null
? enrich( ? enrich(
prefix(assetEntity.toShortString(), "parentAsset"), prefix(assetEntity.toShortString(), "parentAsset"),
@ -112,7 +112,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
: emptyList(); : emptyList();
} }
protected List<String> validateAgainstSubEntities(final HsHostingAssetEntity assetEntity) { protected List<String> validateAgainstSubEntities(final HsHostingAsset assetEntity) {
return enrich( return enrich(
prefix(assetEntity.toShortString(), "config"), prefix(assetEntity.toShortString(), "config"),
stream(propertyValidators) stream(propertyValidators)
@ -124,7 +124,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
// TODO.test: check, if there are any hosting assets which need this validation at all // TODO.test: check, if there are any hosting assets which need this validation at all
private String validateMaxTotalValue( private String validateMaxTotalValue(
final HsHostingAssetEntity hostingAsset, final HsHostingAsset hostingAsset,
final ValidatableProperty<?, ?> propDef) { final ValidatableProperty<?, ?> propDef) {
final var propName = propDef.propertyName(); final var propName = propDef.propertyName();
final var propUnit = ofNullable(propDef.unit()).map(u -> " " + u).orElse(""); final var propUnit = ofNullable(propDef.unit()).map(u -> " " + u).orElse("");
@ -140,7 +140,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
: null; : null;
} }
private List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) { private List<String> validateIdentifierPattern(final HsHostingAsset assetEntity) {
final var expectedIdentifierPattern = identifierPattern(assetEntity); final var expectedIdentifierPattern = identifierPattern(assetEntity);
if (assetEntity.getIdentifier() == null || if (assetEntity.getIdentifier() == null ||
!expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) { !expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) {
@ -151,19 +151,19 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
return Collections.emptyList(); return Collections.emptyList();
} }
protected abstract Pattern identifierPattern(HsHostingAssetEntity assetEntity); protected abstract Pattern identifierPattern(HsHostingAsset assetEntity);
static class ReferenceValidator<S, T> { static class ReferenceValidator<S, T> {
private final HsHostingAssetType.RelationPolicy policy; private final HsHostingAssetType.RelationPolicy policy;
private final Set<T> referencedEntityTypes; private final Set<T> referencedEntityTypes;
private final Function<HsHostingAssetEntity, S> referencedEntityGetter; private final Function<HsHostingAsset, 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 Set<T> referencedEntityTypes, final Set<T> referencedEntityTypes,
final Function<HsHostingAssetEntity, S> referencedEntityGetter, final Function<HsHostingAsset, S> referencedEntityGetter,
final Function<S, T> referencedEntityTypeGetter) { final Function<S, T> referencedEntityTypeGetter) {
this.policy = policy; this.policy = policy;
this.referencedEntityTypes = referencedEntityTypes; this.referencedEntityTypes = referencedEntityTypes;
@ -173,14 +173,14 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
public ReferenceValidator( public ReferenceValidator(
final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType.RelationPolicy policy,
final Function<HsHostingAssetEntity, S> referencedEntityGetter) { final Function<HsHostingAsset, S> referencedEntityGetter) {
this.policy = policy; this.policy = policy;
this.referencedEntityTypes = Set.of(); this.referencedEntityTypes = Set.of();
this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityGetter = referencedEntityGetter;
this.referencedEntityTypeGetter = e -> null; this.referencedEntityTypeGetter = e -> null;
} }
List<String> validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) { List<String> validate(final HsHostingAsset assetEntity, final String referenceFieldName) {
final var actualEntity = referencedEntityGetter.apply(assetEntity); final var actualEntity = referencedEntityGetter.apply(assetEntity);
final var actualEntityType = actualEntity != null ? referencedEntityTypeGetter.apply(actualEntity) : null; final var actualEntityType = actualEntity != null ? referencedEntityTypeGetter.apply(actualEntity) : null;
@ -216,7 +216,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> { static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
AlarmContact(final HsHostingAssetType.RelationPolicy policy) { AlarmContact(final HsHostingAssetType.RelationPolicy policy) {
super(policy, HsHostingAssetEntity::getAlarmContact); super(policy, HsHostingAsset::getAlarmContact);
} }
// hostmaster alert address is implicitly added where neccessary // hostmaster alert address is implicitly added where neccessary

View File

@ -1,6 +1,7 @@
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.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
@ -12,7 +13,7 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.*;
public class HostingAssetEntityValidatorRegistry { public class HostingAssetEntityValidatorRegistry {
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity>> validators = new HashMap<>(); private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAsset>> validators = new HashMap<>();
static { static {
// HOWTO: add (register) new HsHostingAssetType-specific validators // HOWTO: add (register) new HsHostingAssetType-specific validators
register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator()); register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator());
@ -36,14 +37,14 @@ public class HostingAssetEntityValidatorRegistry {
register(IPV6_NUMBER, new HsIPv6NumberHostingAssetValidator()); register(IPV6_NUMBER, new HsIPv6NumberHostingAssetValidator());
} }
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) { private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAsset> validator) {
stream(validator.propertyValidators).forEach( entry -> { stream(validator.propertyValidators).forEach( entry -> {
entry.verifyConsistency(Map.entry(type, validator)); entry.verifyConsistency(Map.entry(type, validator));
}); });
validators.put(type, validator); validators.put(type, validator);
} }
public static HsEntityValidator<HsHostingAssetEntity> forType(final Enum<HsHostingAssetType> type) { public static HsEntityValidator<HsHostingAsset> forType(final Enum<HsHostingAssetType> type) {
if ( validators.containsKey(type)) { if ( validators.containsKey(type)) {
return validators.get(type); return validators.get(type);
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -16,7 +16,7 @@ class HsCloudServerHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$"); return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
} }
} }

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.system.SystemProcess; import net.hostsharing.hsadminng.system.SystemProcess;
import java.util.List; import java.util.List;
@ -59,12 +59,12 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$");
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX));
@ -73,7 +73,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
@Override @Override
@SneakyThrows @SneakyThrows
public List<String> validateContext(final HsHostingAssetEntity assetEntity) { public List<String> validateContext(final HsHostingAsset assetEntity) {
final var result = super.validateContext(assetEntity); final var result = super.validateContext(assetEntity);
// TODO.spec: define which checks should get raised to error level // TODO.spec: define which checks should get raised to error level
@ -87,7 +87,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
return result; return result;
} }
String toZonefileString(final HsHostingAssetEntity assetEntity) { String toZonefileString(final HsHostingAsset assetEntity) {
// TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack // TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack
return """ return """
$ORIGIN {domain}. $ORIGIN {domain}.
@ -104,7 +104,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
.replace("{userRRs}", getPropertyValues(assetEntity, "user-RR") ); .replace("{userRRs}", getPropertyValues(assetEntity, "user-RR") );
} }
private String fqdn(final HsHostingAssetEntity assetEntity) { private String fqdn(final HsHostingAsset assetEntity) {
return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length()-IDENTIFIER_SUFFIX.length()); return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length()-IDENTIFIER_SUFFIX.length());
} }
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -42,12 +42,12 @@ class HsDomainHttpSetupHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$");
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX));

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -20,12 +20,12 @@ class HsDomainMboxSetupHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$");
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX));

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -22,7 +22,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
public List<String> validateEntity(final HsHostingAssetEntity assetEntity) { public List<String> validateEntity(final HsHostingAsset assetEntity) {
// TODO.impl: for newly created entities, check the permission of setting up a domain // TODO.impl: for newly created entities, check the permission of setting up a domain
// //
// reject, if the domain is any of these: // reject, if the domain is any of these:
@ -51,7 +51,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return identifierPattern; return identifierPattern;
} }
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -20,12 +20,12 @@ class HsDomainSmtpSetupHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$"); return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$");
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX)); ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX));

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -11,7 +11,7 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator { class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\.-]*)?$"; // also accepts legacy pac-names private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$"; // also accepts legacy pac-names
private static final String EMAIL_ADDRESS_LOCAL_PART_REGEX = "[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+"; // RFC 5322 private static final String EMAIL_ADDRESS_LOCAL_PART_REGEX = "[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+"; // RFC 5322
private static final String EMAIL_ADDRESS_DOMAIN_PART_REGEX = "[a-zA-Z0-9.-]+"; private static final String EMAIL_ADDRESS_DOMAIN_PART_REGEX = "[a-zA-Z0-9.-]+";
private static final String EMAIL_ADDRESS_FULL_REGEX = "^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "@" + EMAIL_ADDRESS_DOMAIN_PART_REGEX + "$"; private static final String EMAIL_ADDRESS_FULL_REGEX = "^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "@" + EMAIL_ADDRESS_DOMAIN_PART_REGEX + "$";
@ -29,7 +29,7 @@ class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
@ -38,11 +38,11 @@ class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^"+ Pattern.quote(combineIdentifier(assetEntity)) + "$"); return Pattern.compile("^"+ Pattern.quote(combineIdentifier(assetEntity)) + "$");
} }
private static String combineIdentifier(final HsHostingAssetEntity emailAddressAssetEntity) { private static String combineIdentifier(final HsHostingAsset emailAddressAssetEntity) {
return emailAddressAssetEntity.getDirectValue("local-part", String.class) + return emailAddressAssetEntity.getDirectValue("local-part", String.class) +
ofNullable(emailAddressAssetEntity.getDirectValue("sub-domain", String.class)).map(s -> "." + s).orElse("") + ofNullable(emailAddressAssetEntity.getDirectValue("sub-domain", String.class)).map(s -> "." + s).orElse("") +
"@" + "@" +

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -10,10 +10,10 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator { class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator {
private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\.-]*)?$"; // also accepts legacy pac-names private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$"; // also accepts legacy pac-names
private static final String EMAIL_ADDRESS_REGEX = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"; // RFC 5322 private static final String EMAIL_ADDRESS_REGEX = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"; // RFC 5322
private static final String INCLUDE_REGEX = "^:include:/.*$"; private static final String INCLUDE_REGEX = "^:include:/.*$";
private static final String PIPE_REGEX = "^|.*$"; private static final String PIPE_REGEX = "^\\|.*$";
public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322 public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322
HsEMailAliasHostingAssetValidator() { HsEMailAliasHostingAssetValidator() {
@ -26,8 +26,8 @@ class HsEMailAliasHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9][a-z0-9\\.-]*$"); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9][a-z0-9\\._-]*$");
} }
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -20,7 +20,7 @@ class HsIPv4NumberHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return IPV4_REGEX; return IPV4_REGEX;
} }
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -24,7 +24,7 @@ class HsIPv6NumberHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
public List<String> validateEntity(final HsHostingAssetEntity assetEntity) { public List<String> validateEntity(final HsHostingAsset assetEntity) {
final var violations = super.validateEntity(assetEntity); final var violations = super.validateEntity(assetEntity);
if (!isValidIPv6Address(assetEntity.getIdentifier())) { if (!isValidIPv6Address(assetEntity.getIdentifier())) {
@ -35,7 +35,7 @@ class HsIPv6NumberHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return IPV6_REGEX; return IPV6_REGEX;
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -54,7 +54,7 @@ class HsManagedServerHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$"); return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
} }
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -11,11 +11,11 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator
super( super(
MANAGED_WEBSPACE, MANAGED_WEBSPACE,
AlarmContact.isOptional(), AlarmContact.isOptional(),
NO_EXTRA_PROPERTIES); NO_EXTRA_PROPERTIES); // TODO.impl: groupid missing, should be equal to main user
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var prefixPattern = final var prefixPattern =
!assetEntity.isLoaded() !assetEntity.isLoaded()
? assetEntity.getRelatedProject().getDebitor().getDefaultPrefix() ? assetEntity.getRelatedProject().getDebitor().getDefaultPrefix()

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -18,7 +18,7 @@ class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -19,7 +19,7 @@ class HsMariaDbInstanceHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile( return Pattern.compile(
"^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() "^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier()
+ DEFAULT_INSTANCE_IDENTIFIER_SUFFIX) + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX)
@ -27,7 +27,7 @@ class HsMariaDbInstanceHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier( ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hash.HashGenerator;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -26,7 +26,7 @@ class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator {
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -21,7 +21,7 @@ class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValida
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier(); final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
} }

View File

@ -1,6 +1,6 @@
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.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -21,7 +21,7 @@ class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityVali
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
return Pattern.compile( return Pattern.compile(
"^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() "^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier()
+ DEFAULT_INSTANCE_IDENTIFIER_SUFFIX) + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX)
@ -29,7 +29,7 @@ class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityVali
} }
@Override @Override
public void preprocessEntity(final HsHostingAssetEntity entity) { public void preprocessEntity(final HsHostingAsset entity) {
super.preprocessEntity(entity); super.preprocessEntity(entity);
if (entity.getIdentifier() == null) { if (entity.getIdentifier() == null) {
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier( ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hash.HashGenerator;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -26,7 +26,7 @@ class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$"); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
} }

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hash.HashGenerator;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
@ -9,7 +9,6 @@ import jakarta.persistence.EntityManager;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
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.IntegerProperty.integerProperty;
import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty; import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty; import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
@ -24,35 +23,38 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator {
AlarmContact.isOptional(), AlarmContact.isOptional(),
booleanProperty("locked").readOnly(), booleanProperty("locked").readOnly(),
integerProperty("userid").computedBy(HsUnixUserHostingAssetValidator::computeUserId), integerProperty("userid").readOnly().initializedBy(HsUnixUserHostingAssetValidator::computeUserId),
integerProperty("SSD hard quota").unit("MB").maxFrom("SSD").withFactor(1024).optional(), integerProperty("SSD hard quota").unit("MB").maxFrom("SSD").withFactor(1024).optional(),
integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(), integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(),
integerProperty("HDD hard quota").unit("MB").maxFrom("HDD").withFactor(1024).optional(), integerProperty("HDD hard quota").unit("MB").maxFrom("HDD").withFactor(1024).optional(),
integerProperty("HDD soft quota").unit("MB").maxFrom("HDD hard quota").optional(), integerProperty("HDD soft quota").unit("MB").maxFrom("HDD hard quota").optional(),
enumerationProperty("shell") stringProperty("shell")
.values("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd") // TODO.spec: do we want to change them all to /usr/bin/, also in import?
.provided("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd")
.withDefault("/bin/false"), .withDefault("/bin/false"),
stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir), stringProperty("homedir").readOnly().renderedBy(HsUnixUserHostingAssetValidator::computeHomedir),
stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(), stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(),
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly()); passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly());
// TODO.spec: public SSH keys? (only if hsadmin-ng is only accessible with 2FA) // TODO.spec: public SSH keys? (only if hsadmin-ng is only accessible with 2FA)
} }
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier(); final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\.-]+$"); return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\._-]+$");
} }
private static String computeHomedir(final EntityManager em, final PropertiesProvider propertiesProvider) { private static String computeHomedir(final EntityManager em, final PropertiesProvider propertiesProvider) {
final var entity = (HsHostingAssetEntity) propertiesProvider; final var entity = (HsHostingAsset) propertiesProvider;
final var webspaceName = entity.getParentAsset().getIdentifier(); final var webspaceName = entity.getParentAsset().getIdentifier();
return "/home/pacs/" + webspaceName return "/home/pacs/" + webspaceName
+ "/users/" + entity.getIdentifier().substring(webspaceName.length()+DASH_LENGTH); + "/users/" + entity.getIdentifier().substring(webspaceName.length()+DASH_LENGTH);
} }
private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) { private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) {
return Math.toIntExact((Long) em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')").getSingleResult()); final Object result = em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')", Integer.class)
.getSingleResult();
return (Integer) result;
} }
} }

View File

@ -1,9 +1,9 @@
### HsHostingAssetEntity-Validation ### HsHostingAsset-Validation
There is just a single `HsHostingAssetEntity` class for all types of hosting assets like Managed-Server, Managed-Webspace, Unix-Users, Databases etc. These are distinguished by `HsHostingAssetType HsHostingAssetEntity.type`. There is just a single `HsHostingAsset` interface and `HsHostingAssetEntity` entity for all types of hosting assets like Managed-Server, Managed-Webspace, Unix-Users, Databases etc. These are distinguished by `HsHostingAssetType HsHostingAsset.type`.
For each of these types, a distinct validator has to be For each of these types, a distinct validator has to be
implemented as a subclass of `HsHostingAssetEntityValidator` which needs to be registered (see `HsHostingAssetEntityValidatorRegistry`) for the relevant type(s). implemented as a subclass of `HsHostingAssetValidator` which needs to be registered (see `HsHostingAssetValidatorRegistry`) for the relevant type(s).
### Kinds of Validations ### Kinds of Validations
@ -21,7 +21,7 @@ References in this context are:
- the Assigned-To-Hosting-Asset and - the Assigned-To-Hosting-Asset and
- the Contact. - the Contact.
The first parameters of the `HsHostingAssetEntityValidator` superclass take rule descriptors for these references. These are all Subclasses fo The first parameters of the `HsHostingAssetValidator` superclass take rule descriptors for these references. These are all Subclasses fo
### Validation Order ### Validation Order
@ -37,4 +37,4 @@ In general, the validation es executed in this order:
2. the limits of the parent entity (parent asset + booking item) 2. the limits of the parent entity (parent asset + booking item)
3. limits against the own own-sub-entities 3. limits against the own own-sub-entities
This implementation can be found in `HsHostingAssetEntityValidator.validate`. This implementation can be found in `HsHostingAssetValidator.validate`.

View File

@ -14,6 +14,9 @@ import java.util.stream.Collectors;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static net.hostsharing.hsadminng.hs.validation.ValidatableProperty.ComputeMode.IN_INIT;
import static net.hostsharing.hsadminng.hs.validation.ValidatableProperty.ComputeMode.IN_PREP;
import static net.hostsharing.hsadminng.hs.validation.ValidatableProperty.ComputeMode.IN_REVAMP;
// TODO.refa: rename to HsEntityProcessor, also subclasses // TODO.refa: rename to HsEntityProcessor, also subclasses
public abstract class HsEntityValidator<E extends PropertiesProvider> { public abstract class HsEntityValidator<E extends PropertiesProvider> {
@ -109,7 +112,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
public void prepareProperties(final EntityManager em, final E entity) { public void prepareProperties(final EntityManager em, final E entity) {
stream(propertyValidators).forEach(p -> { stream(propertyValidators).forEach(p -> {
if (!p.isReadOnly() && p.isComputed()) { if (p.isComputed(IN_PREP) || p.isComputed(IN_INIT) && !entity.isLoaded() ) {
entity.directProps().put(p.propertyName, p.compute(em, entity)); entity.directProps().put(p.propertyName, p.compute(em, entity));
} }
}); });
@ -120,7 +123,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
stream(propertyValidators).forEach(p -> { stream(propertyValidators).forEach(p -> {
if (p.isWriteOnly()) { if (p.isWriteOnly()) {
copy.remove(p.propertyName); copy.remove(p.propertyName);
} else if (p.isReadOnly() && p.isComputed()) { } else if (p.isComputed(IN_REVAMP)) {
copy.put(p.propertyName, p.compute(em, entity)); copy.put(p.propertyName, p.compute(em, entity));
} }
}); });

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.validation; package net.hostsharing.hsadminng.hs.validation;
import lombok.Setter;
import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hash.HashGenerator;
import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm; import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm;
import lombok.Setter;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -13,7 +13,10 @@ import static net.hostsharing.hsadminng.mapper.Array.insertNewEntriesAfterExisti
@Setter @Setter
public class PasswordProperty extends StringProperty<PasswordProperty> { public class PasswordProperty extends StringProperty<PasswordProperty> {
private static final String[] KEY_ORDER = insertNewEntriesAfterExistingEntry(StringProperty.KEY_ORDER, "computed", "hashedUsing"); private static final String[] KEY_ORDER = insertNewEntriesAfterExistingEntry(
StringProperty.KEY_ORDER,
"computed",
"hashedUsing");
private Algorithm hashedUsing; private Algorithm hashedUsing;
@ -34,8 +37,9 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
public PasswordProperty hashedUsing(final Algorithm algorithm) { public PasswordProperty hashedUsing(final Algorithm algorithm) {
this.hashedUsing = algorithm; this.hashedUsing = algorithm;
computedBy((em, entity) computedBy(
-> ofNullable(entity.getDirectValue(propertyName, String.class)) ComputeMode.IN_PREP,
(em, entity) -> ofNullable(entity.getDirectValue(propertyName, String.class))
.map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password)) .map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password))
.orElse(null)); .orElse(null));
return self(); return self();
@ -71,7 +75,8 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
final long groupsCovered = Stream.of(hasLowerCase, hasUpperCase, hasDigit, hasSpecialChar).filter(v -> v).count(); final long groupsCovered = Stream.of(hasLowerCase, hasUpperCase, hasDigit, hasSpecialChar).filter(v -> v).count();
if (groupsCovered < 3) { if (groupsCovered < 3) {
result.add(propertyName + "' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"); result.add(propertyName
+ "' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters");
} }
if (containsColon) { if (containsColon) {
result.add(propertyName + "' must not contain colon (':')"); result.add(propertyName + "' must not contain colon (':')");

View File

@ -4,6 +4,7 @@ import java.util.Map;
public interface PropertiesProvider { public interface PropertiesProvider {
boolean isLoaded();
Map<String, Object> directProps(); Map<String, Object> directProps();
Object getContextValue(final String propName); Object getContextValue(final String propName);
@ -11,6 +12,10 @@ public interface PropertiesProvider {
return cast(propName, directProps().get(propName), clazz, null); return cast(propName, directProps().get(propName), clazz, null);
} }
default <T> T getDirectValue(final String propName, final Class<T> clazz, final T defaultValue) {
return cast(propName, directProps().get(propName), clazz, defaultValue);
}
default <T> T getContextValue(final String propName, final Class<T> clazz) { default <T> T getContextValue(final String propName, final Class<T> clazz) {
return cast(propName, getContextValue(propName), clazz, null); return cast(propName, getContextValue(propName), clazz, null);
} }

View File

@ -48,11 +48,17 @@ public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T
private Set<String> requiresAtMaxOneOf; private Set<String> requiresAtMaxOneOf;
private T defaultValue; private T defaultValue;
protected enum ComputeMode {
IN_INIT,
IN_PREP,
IN_REVAMP
}
@JsonIgnore @JsonIgnore
private BiFunction<EntityManager, PropertiesProvider, T> computedBy; private BiFunction<EntityManager, PropertiesProvider, T> computedBy;
@Accessors(makeFinal = true, chain = true, fluent = false) @Accessors(makeFinal = true, chain = true, fluent = false)
private boolean computed; // used in descriptor, because computedBy cannot be rendered to a text string private ComputeMode computed; // name 'computed' instead 'computeMode' for better readability in property description
@Accessors(makeFinal = true, chain = true, fluent = false) @Accessors(makeFinal = true, chain = true, fluent = false)
private boolean readOnly; private boolean readOnly;
@ -236,8 +242,8 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
protected abstract void validate(final List<String> result, final T propValue, final PropertiesProvider propProvider); protected abstract void validate(final List<String> result, final T propValue, final PropertiesProvider propProvider);
public void verifyConsistency(final Map.Entry<? extends Enum<?>, ?> typeDef) { public void verifyConsistency(final Map.Entry<? extends Enum<?>, ?> typeDef) {
if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && !computed) { if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null && !readOnly && defaultValue == null) {
throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .computed(...), .readOnly(), .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" ); throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .readOnly(), .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" );
} }
} }
@ -302,12 +308,24 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
.toList(); .toList();
} }
public P computedBy(final BiFunction<EntityManager, PropertiesProvider, T> compute) { public P initializedBy(final BiFunction<EntityManager, PropertiesProvider, T> compute) {
return computedBy(ComputeMode.IN_INIT, compute);
}
public P renderedBy(final BiFunction<EntityManager, PropertiesProvider, T> compute) {
return computedBy(ComputeMode.IN_REVAMP, compute);
}
protected P computedBy(final ComputeMode computeMode, final BiFunction<EntityManager, PropertiesProvider, T> compute) {
this.computedBy = compute; this.computedBy = compute;
this.computed = true; this.computed = computeMode;
return self(); return self();
} }
public boolean isComputed(final ComputeMode computeMode) {
return computed == computeMode;
}
public <E extends PropertiesProvider> T compute(final EntityManager em, final E entity) { public <E extends PropertiesProvider> T compute(final EntityManager em, final E entity) {
return computedBy.apply(em, entity); return computedBy.apply(em, entity);
} }

View File

@ -16,9 +16,8 @@ import static java.lang.Boolean.TRUE;
public final class Stringify<B> { public final class Stringify<B> {
private final Class<B> clazz;
private final String name; private final String name;
private Function<B, ?> idProp; private Function<? extends B, ?> idProp;
private final List<Property<B>> props = new ArrayList<>(); private final List<Property<B>> props = new ArrayList<>();
private String separator = ", "; private String separator = ", ";
private Boolean quotedValues = null; private Boolean quotedValues = null;
@ -31,8 +30,16 @@ public final class Stringify<B> {
return new Stringify<>(clazz, null); return new Stringify<>(clazz, null);
} }
public <T extends B> Stringify<T> using(final Class<T> subClass) {
//noinspection unchecked
return (Stringify<T>) new Stringify<T>(subClass, null)
.withIdProp(cast(idProp))
.withProps(cast(props))
.withSeparator(separator)
.quotedValues(quotedValues);
}
private Stringify(final Class<B> clazz, final String name) { private Stringify(final Class<B> clazz, final String name) {
this.clazz = clazz;
if (name != null) { if (name != null) {
this.name = name; this.name = name;
} else { } else {
@ -45,7 +52,7 @@ public final class Stringify<B> {
} }
} }
public Stringify<B> withIdProp(final Function<B, ?> getter) { public Stringify<B> withIdProp(final Function<? extends B, ?> getter) {
idProp = getter; idProp = getter;
return this; return this;
} }
@ -60,6 +67,11 @@ public final class Stringify<B> {
return this; return this;
} }
private Stringify<B> withProps(final List<Property<B>> props) {
this.props.addAll(props);
return this;
}
public String apply(@NotNull B object) { public String apply(@NotNull B object) {
final var propValues = props.stream() final var propValues = props.stream()
.map(prop -> PropertyValue.of(prop, prop.getter.apply(object))) .map(prop -> PropertyValue.of(prop, prop.getter.apply(object)))
@ -74,7 +86,7 @@ public final class Stringify<B> {
.map(propVal -> propName(propVal, "=") + optionallyQuoted(propVal)) .map(propVal -> propName(propVal, "=") + optionallyQuoted(propVal))
.collect(Collectors.joining(separator)); .collect(Collectors.joining(separator));
return idProp != null return idProp != null
? name + "(" + idProp.apply(object) + ": " + propValues + ")" ? name + "(" + idProp.apply(cast(object)) + ": " + propValues + ")"
: name + "(" + propValues + ")"; : name + "(" + propValues + ")";
} }
@ -106,6 +118,11 @@ public final class Stringify<B> {
return this; return this;
} }
private <T> T cast(final Object object) {
//noinspection unchecked
return (T)object;
}
private record Property<B>(String name, Function<B, ?> getter) {} private record Property<B>(String name, Function<B, ?> getter) {}
private record PropertyValue<B>(Property<B> prop, Object rawValue, String value) { private record PropertyValue<B>(Property<B> prop, Object rawValue, String value) {

View File

@ -39,7 +39,7 @@ class HsEMailAddressHostingAssetValidatorUnitTest {
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=string, propertyName=local-part, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], required=true}", "{type=string, propertyName=local-part, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$], required=true}",
"{type=string, propertyName=sub-domain, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$]}", "{type=string, propertyName=sub-domain, matchesRegEx=[^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$]}",
"{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$], maxLength=320}, required=true, minLength=1}"); "{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$], maxLength=320}, required=true, minLength=1}");
} }
@Test @Test
@ -73,7 +73,7 @@ class HsEMailAddressHostingAssetValidatorUnitTest {
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ADDRESS:test@example.org.config.local-part' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowed' does not match", "'EMAIL_ADDRESS:test@example.org.config.local-part' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowed' does not match",
"'EMAIL_ADDRESS:test@example.org.config.sub-domain' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowedeither' does not match", "'EMAIL_ADDRESS:test@example.org.config.sub-domain' is expected to match any of [^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+$] but 'no@allowedeither' does not match",
"'EMAIL_ADDRESS:test@example.org.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$] but 'garbage' does not match any"); "'EMAIL_ADDRESS:test@example.org.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$] but 'garbage' does not match any");
} }
@Test @Test

View File

@ -22,7 +22,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
// then // then
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$], maxLength=320}, required=true, minLength=1}"); "{type=string[], propertyName=target, elementsOf={type=string, propertyName=target, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$, ^:include:/.*$, ^\\|.*$], maxLength=320}, required=true, minLength=1}");
} }
@Test @Test
@ -63,7 +63,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ALIAS:xyz00-office.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$] but 'garbage' does not match any"); "'EMAIL_ALIAS:xyz00-office.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$, ^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$, ^:include:/.*$, ^\\|.*$] but 'garbage' does not match any");
} }
@Test @Test
@ -84,7 +84,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'identifier' expected to match '^xyz00$|^xyz00-[a-z0-9]+$', but is 'abc00-office'"); "'identifier' expected to match '^xyz00$|^xyz00-[a-z0-9][a-z0-9\\._-]*$', but is 'abc00-office'");
} }
@Test @Test

View File

@ -49,7 +49,7 @@ class HsMariaDbUserHostingAssetValidatorUnitTest {
// then // then
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=MYSQL_NATIVE, undisclosed=true}" "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=IN_PREP, hashedUsing=MYSQL_NATIVE, undisclosed=true}"
); );
} }

View File

@ -52,7 +52,7 @@ class HsPostgreSqlUserHostingAssetValidatorUnitTest {
// then // then
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SCRAM_SHA256, undisclosed=true}" "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=IN_PREP, hashedUsing=SCRAM_SHA256, undisclosed=true}"
); );
} }

View File

@ -3,9 +3,14 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hash.HashGenerator; import net.hostsharing.hsadminng.hash.HashGenerator;
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 net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.util.HashMap; import java.util.HashMap;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -16,7 +21,10 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANA
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 net.hostsharing.hsadminng.mapper.PatchMap.entry;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@ExtendWith(MockitoExtension.class)
class HsUnixUserHostingAssetValidatorUnitTest { class HsUnixUserHostingAssetValidatorUnitTest {
private final HsHostingAssetEntity TEST_MANAGED_SERVER_HOSTING_ASSET = HsHostingAssetEntity.builder() private final HsHostingAssetEntity TEST_MANAGED_SERVER_HOSTING_ASSET = HsHostingAssetEntity.builder()
@ -44,7 +52,17 @@ class HsUnixUserHostingAssetValidatorUnitTest {
))) )))
.build(); .build();
private EntityManager em = null; // not actually needed in these test cases @Mock
EntityManager em;
@BeforeEach
void initMocks() {
final var nativeQueryMock = mock(Query.class);
lenient().when(nativeQueryMock.getSingleResult()).thenReturn(12345678);
lenient().when(em.createNativeQuery("SELECT nextval('hs_hosting_asset_unixuser_system_id_seq')", Integer.class))
.thenReturn(nativeQueryMock);
}
@Test @Test
void preparesUnixUser() { void preparesUnixUser() {
@ -61,7 +79,8 @@ class HsUnixUserHostingAssetValidatorUnitTest {
entry("SSD hard quota", 50), entry("SSD hard quota", 50),
entry("SSD soft quota", 40), entry("SSD soft quota", 40),
entry("totpKey", "0x123456789abcdef01234"), entry("totpKey", "0x123456789abcdef01234"),
entry("password", "$6$Ly3LbsArtL5u4EVt$i/ayIEvm0y4bjkFB6wbg8imbRIaw4mAA4gqYRVyoSkj.iIxJKS3KiRkSjP8gweNcpKL0Q0N31EadT8fCnWErL.") entry("password", "$6$Ly3LbsArtL5u4EVt$i/ayIEvm0y4bjkFB6wbg8imbRIaw4mAA4gqYRVyoSkj.iIxJKS3KiRkSjP8gweNcpKL0Q0N31EadT8fCnWErL."),
entry("userid", 12345678)
)); ));
} }
@ -107,11 +126,9 @@ class HsUnixUserHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'UNIX_USER:abc00-temp.config.SSD hard quota' is expected to be at most 50 but is 100",
"'UNIX_USER:abc00-temp.config.SSD soft quota' is expected to be at most 100 but is 200", "'UNIX_USER:abc00-temp.config.SSD soft quota' is expected to be at most 100 but is 200",
"'UNIX_USER:abc00-temp.config.HDD hard quota' is expected to be at most 0 but is 100", "'UNIX_USER:abc00-temp.config.HDD hard quota' is expected to be at most 0 but is 100",
"'UNIX_USER:abc00-temp.config.HDD soft quota' is expected to be at most 100 but is 200", "'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.homedir' is readonly but given as '/is/read-only'",
"'UNIX_USER:abc00-temp.config.totpKey' is expected to match any of [^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",
"'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' length is expected to be at min 8 but length of provided value is 5",
@ -134,7 +151,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactly( assertThat(result).containsExactly(
"'identifier' expected to match '^abc00$|^abc00-[a-z0-9]+$', but is 'xyz99-temp'"); "'identifier' expected to match '^abc00$|^abc00-[a-z0-9\\._-]+$', but is 'xyz99-temp'");
} }
@Test @Test
@ -165,14 +182,16 @@ class HsUnixUserHostingAssetValidatorUnitTest {
// then // then
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder( assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
"{type=integer, propertyName=SSD hard quota, unit=GB, maxFrom=SSD}", "{type=boolean, propertyName=locked, readOnly=true}",
"{type=integer, propertyName=SSD soft quota, unit=GB, maxFrom=SSD hard quota}", "{type=integer, propertyName=userid, readOnly=true, computed=IN_INIT}",
"{type=integer, propertyName=HDD hard quota, unit=GB, maxFrom=HDD}", "{type=integer, propertyName=SSD hard quota, unit=MB, maxFrom=SSD}",
"{type=integer, propertyName=HDD soft quota, unit=GB, maxFrom=HDD hard quota}", "{type=integer, propertyName=SSD soft quota, unit=MB, maxFrom=SSD 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=integer, propertyName=HDD hard quota, unit=MB, maxFrom=HDD}",
"{type=string, propertyName=homedir, readOnly=true, computed=true}", "{type=integer, propertyName=HDD soft quota, unit=MB, maxFrom=HDD hard quota}",
"{type=string, propertyName=shell, provided=[/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=IN_REVAMP}",
"{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=LINUX_SHA512, undisclosed=true}" "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=IN_PREP, hashedUsing=LINUX_SHA512, undisclosed=true}"
); );
} }
} }

View File

@ -26,15 +26,19 @@ import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.lang.Boolean.parseBoolean; import static java.lang.Boolean.parseBoolean;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
@ -70,7 +74,7 @@ public class CsvDataImport extends ContextBasedTest {
@MockBean @MockBean
HttpServletRequest request; HttpServletRequest request;
private static final List<AssertionError> errors = new ArrayList<>(); static final List<String> errors = new ArrayList<>();
public List<String[]> readAllLines(Reader reader) throws Exception { public List<String[]> readAllLines(Reader reader) throws Exception {
@ -136,7 +140,7 @@ public class CsvDataImport extends ContextBasedTest {
try { try {
final var asString = entity.toString(); final var asString = entity.toString();
if ( asString.contains("'null null, null'") || asString.equals("person()")) { if ( asString.contains("'null null, null'") || asString.equals("person()")) {
System.err.println("skipping to persist empty record-id " + id + " #" + entity.hashCode() + ": " + entity); errors.add("skipping to persist empty record-id " + id + " #" + entity.hashCode() + ": " + entity);
return entity; return entity;
} }
//System.out.println("persisting #" + entity.hashCode() + ": " + entity); //System.out.println("persisting #" + entity.hashCode() + ": " + entity);
@ -145,8 +149,8 @@ public class CsvDataImport extends ContextBasedTest {
// em.flush(); // makes it slow, but produces better error messages // em.flush(); // makes it slow, but produces better error messages
// System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); // System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid());
} catch (Exception exc) { } catch (Exception exc) {
System.err.println("failed to persist #" + entity.hashCode() + ": " + entity); errors.add("failed to persist #" + entity.hashCode() + ": " + entity);
System.err.println(exc); errors.add(exc.toString());
} }
return entity; return entity;
} }
@ -225,12 +229,33 @@ public class CsvDataImport extends ContextBasedTest {
try { try {
assertion.run(); assertion.run();
} catch (final AssertionError exc) { } catch (final AssertionError exc) {
errors.add(exc); errors.add(exc.toString());
} }
} }
void logErrors() { void logErrors() {
assumeThat(errors).isEmpty(); assertThat(errors).isEmpty();
}
void expectErrors(final String... expectedErrors) {
assertContainsExactlyInAnyOrderIgnoringWhitespace(errors, expectedErrors);
}
private static class IgnoringWhitespaceComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
return s1.replaceAll("\\s", "").compareTo(s2.replaceAll("\\s", ""));
}
}
public static void assertContainsExactlyInAnyOrderIgnoringWhitespace(final List<String> expected, final List<String> actual) {
final var sortedExpected = expected.stream().map(m -> m.replaceAll("\\s", "")).toList();
final var sortedActual = actual.stream().map(m -> m.replaceAll("\\s", "")).toArray(String[]::new);
assertThat(sortedExpected).containsExactlyInAnyOrder(sortedActual);
}
public static void assertContainsExactlyInAnyOrderIgnoringWhitespace(final List<String> expected, final String... actual) {
assertContainsExactlyInAnyOrderIgnoringWhitespace(expected, asList(actual));
} }
} }
@ -298,12 +323,17 @@ class Record {
} }
} }
@Retention(RetentionPolicy.RUNTIME)
@interface ContinueOnFailure {
}
class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback {
private static boolean previousTestsPassed = true; private static boolean previousTestsPassed = true;
public void testFailed(ExtensionContext context, Throwable cause) { @Override
previousTestsPassed = false; public void testFailed(final ExtensionContext context, final Throwable cause) {
previousTestsPassed = previousTestsPassed && context.getElement().map(e -> e.isAnnotationPresent(ContinueOnFailure.class)).orElse(false);
} }
@Override @Override

View File

@ -0,0 +1,114 @@
package net.hostsharing.hsadminng.hs.migration;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import org.hibernate.annotations.Type;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PostLoad;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.persistence.Version;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Builder
@Entity
@Table(name = "hs_hosting_asset")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class HsHostingAssetRawEntity implements HsHostingAsset {
@Id
@GeneratedValue
private UUID uuid;
@Version
private int version;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bookingitemuuid")
private HsBookingItemEntity bookingItem;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parentassetuuid")
private HsHostingAssetRawEntity parentAsset;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "assignedtoassetuuid")
private HsHostingAssetRawEntity assignedToAsset;
@Column(name = "type")
@Enumerated(EnumType.STRING)
private HsHostingAssetType type;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "alarmcontactuuid")
private HsOfficeContactEntity alarmContact;
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
private List<HsHostingAssetRawEntity> subHostingAssets;
@Column(name = "identifier")
private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc
@Column(name = "caption")
private String caption;
@Builder.Default
@Setter(AccessLevel.NONE)
@Type(JsonType.class)
@Column(columnDefinition = "config")
private Map<String, Object> config = new HashMap<>();
@Transient
private PatchableMapWrapper<Object> configWrapper;
@Transient
private boolean isLoaded;
@PostLoad
public void markAsLoaded() {
this.isLoaded = true;
}
public PatchableMapWrapper<Object> getConfig() {
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config);
}
@Override
public Map<String, Object> directProps() {
return config;
}
@Override
public String toString() {
return stringify.using(HsHostingAssetRawEntity.class).apply(this);
}
}

View File

@ -6,7 +6,6 @@ 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.validators.HsBookingItemEntityValidatorRegistry; import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
@ -97,13 +96,14 @@ public class ImportHostingAssets extends ImportOfficeData {
static final Integer HIVE_ID_OFFSET = 2000000; static final Integer HIVE_ID_OFFSET = 2000000;
static final Integer PACKET_ID_OFFSET = 3000000; static final Integer PACKET_ID_OFFSET = 3000000;
static final Integer UNIXUSER_ID_OFFSET = 4000000; static final Integer UNIXUSER_ID_OFFSET = 4000000;
static final Integer EMAILALIAS_ID_OFFSET = 5000000;
record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference<HsHostingAssetEntity> serverRef) {} record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference<HsHostingAssetRawEntity> serverRef) {}
static Map<Integer, HsBookingProjectEntity> bookingProjects = new WriteOnceMap<>(); static Map<Integer, HsBookingProjectEntity> bookingProjects = new WriteOnceMap<>();
static Map<Integer, HsBookingItemEntity> bookingItems = new WriteOnceMap<>(); static Map<Integer, HsBookingItemEntity> bookingItems = new WriteOnceMap<>();
static Map<Integer, Hive> hives = new WriteOnceMap<>(); static Map<Integer, Hive> hives = new WriteOnceMap<>();
static Map<Integer, HsHostingAssetEntity> hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type? static Map<Integer, HsHostingAssetRawEntity> hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type?
@Test @Test
@Order(11010) @Order(11010)
@ -135,11 +135,11 @@ public class ImportHostingAssets extends ImportOfficeData {
// no contacts yet => mostly null values // no contacts yet => mostly null values
assertThat(firstOfEachType(5, IPV4_NUMBER)).isEqualToIgnoringWhitespace(""" assertThat(firstOfEachType(5, IPV4_NUMBER)).isEqualToIgnoringWhitespace("""
{ {
1000363=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.34), 1000363=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.34),
1000381=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.52), 1000381=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.52),
1000402=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.73), 1000402=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.73),
1000433=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.104), 1000433=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.104),
1000457=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.128) 1000457=HsHostingAssetRawEntity(IPV4_NUMBER, 83.223.95.128)
} }
"""); """);
} }
@ -190,13 +190,13 @@ public class ImportHostingAssets extends ImportOfficeData {
assertThat(firstOfEachType(3, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)).isEqualToIgnoringWhitespace(""" assertThat(firstOfEachType(3, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)).isEqualToIgnoringWhitespace("""
{ {
3000630=HsHostingAssetEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), 3000630=HsHostingAssetRawEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00),
3000968=HsHostingAssetEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), 3000968=HsHostingAssetRawEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061),
3000978=HsHostingAssetEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), 3000978=HsHostingAssetRawEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050),
3001061=HsHostingAssetEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), 3001061=HsHostingAssetRawEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068),
3001094=HsHostingAssetEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), 3001094=HsHostingAssetRawEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00),
3001112=HsHostingAssetEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), 3001112=HsHostingAssetRawEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00),
3023611=HsHostingAssetEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) 3023611=HsHostingAssetRawEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097)
} }
"""); """);
assertThat(firstOfEachType( assertThat(firstOfEachType(
@ -236,15 +236,15 @@ public class ImportHostingAssets extends ImportOfficeData {
assertThat(firstOfEachType(5, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)) assertThat(firstOfEachType(5, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE))
.isEqualToIgnoringWhitespace(""" .isEqualToIgnoringWhitespace("""
{ {
3000630=HsHostingAssetEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), 3000630=HsHostingAssetRawEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00),
3000968=HsHostingAssetEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), 3000968=HsHostingAssetRawEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061),
3000978=HsHostingAssetEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), 3000978=HsHostingAssetRawEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050),
3001061=HsHostingAssetEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), 3001061=HsHostingAssetRawEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068),
3001094=HsHostingAssetEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), 3001094=HsHostingAssetRawEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00),
3001112=HsHostingAssetEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), 3001112=HsHostingAssetRawEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00),
3001447=HsHostingAssetEntity(MANAGED_SERVER, vm1093, HA vm1093, D-1000000:hsh default project:BI vm1093), 3001447=HsHostingAssetRawEntity(MANAGED_SERVER, vm1093, HA vm1093, D-1000000:hsh default project:BI vm1093),
3019959=HsHostingAssetEntity(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00), 3019959=HsHostingAssetRawEntity(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00),
3023611=HsHostingAssetEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) 3023611=HsHostingAssetRawEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097)
} }
"""); """);
assertThat(firstOfEachType( assertThat(firstOfEachType(
@ -286,20 +286,20 @@ public class ImportHostingAssets extends ImportOfficeData {
// no contacts yet => mostly null values // no contacts yet => mostly null values
assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace("""
{ {
4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}), 4005803=HsHostingAssetRawEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102090}),
4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}), 4005805=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102091}),
4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102093}), 4005809=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "shell": "/bin/bash", "userid": 102093}),
4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}), 4005811=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102094}),
4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}), 4005813=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102095}),
4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}), 4005835=HsHostingAssetRawEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "shell": "/usr/bin/passwd", "userid": 102106}),
4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}), 4005964=HsHostingAssetRawEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102147}),
4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}), 4005966=HsHostingAssetRawEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "shell": "/bin/bash", "userid": 102148}),
4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}), 4005990=HsHostingAssetRawEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 102160}),
4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 10003}), 4100705=HsHostingAssetRawEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 10003}),
4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}), 4100824=HsHostingAssetRawEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 10000}),
4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}), 4167846=HsHostingAssetRawEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/false", "userid": 110568}),
4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}), 4169546=HsHostingAssetRawEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110593}),
4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594}) 4169596=HsHostingAssetRawEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594})
} }
"""); """);
} }
@ -323,12 +323,16 @@ public class ImportHostingAssets extends ImportOfficeData {
// no contacts yet => mostly null values // no contacts yet => mostly null values
assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace(""" assertThat(firstOfEachType(15, EMAIL_ALIAS)).isEqualToIgnoringWhitespace("""
{ {
4002403=HsHostingAssetEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}), 5002403=HsHostingAssetRawEntity(EMAIL_ALIAS, lug00, lug00, MANAGED_WEBSPACE:lug00, { "target": "[michael.mellis@example.com]"}),
4002405=HsHostingAssetEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}), 5002405=HsHostingAssetRawEntity(EMAIL_ALIAS, lug00-wla-listar, lug00-wla-listar, MANAGED_WEBSPACE:lug00, { "target": "[|/home/pacs/lug00/users/in/mailinglist/listar]"}),
4002429=HsHostingAssetEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}), 5002429=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00, mim00, MANAGED_WEBSPACE:mim00, { "target": "[mim12-mi@mim12.hostsharing.net]"}),
4002431=HsHostingAssetEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}), 5002431=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-abruf, mim00-abruf, MANAGED_WEBSPACE:mim00, { "target": "[michael.mellis@hostsharing.net]"}),
4002449=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}), 5002449=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-hhfx, mim00-hhfx, MANAGED_WEBSPACE:mim00, { "target": "[mim00-hhfx, |/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l]"}),
4002451=HsHostingAssetEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}) 5002451=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-hhfx-l, mim00-hhfx-l, MANAGED_WEBSPACE:mim00, { "target": "[:include:/home/pacs/mim00/etc/hhfx.list]"}),
5002452=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, { "target": "[]"}),
5002453=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, { "target": "[]"}),
5002454=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, { "target": "[/dev/null]"}),
5002455=HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-1_with_space, mim00-1_with_space, MANAGED_WEBSPACE:mim00, { "target": "[|/home/pacs/mim00/install/corpslistar/listar]"})
} }
"""); """);
} }
@ -336,31 +340,39 @@ public class ImportHostingAssets extends ImportOfficeData {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@Test @Test
@Order(11400) @Order(18010)
void validateBookingItems() { void validateBookingItems() {
bookingItems.forEach((id, bi) -> { bookingItems.forEach((id, bi) -> {
try { try {
HsBookingItemEntityValidatorRegistry.validated(bi); HsBookingItemEntityValidatorRegistry.validated(bi);
} catch (final Exception exc) { } catch (final Exception exc) {
System.err.println("validation failed for id:" + id + "( " + bi + "): " + exc.getMessage()); errors.add("validation failed for id:" + id + "( " + bi + "): " + exc.getMessage());
} }
}); });
} }
@Test @Test
@Order(11410) @Order(18020)
void validateHostingAssets() { void validateHostingAssets() {
hostingAssets.forEach((id, ha) -> { hostingAssets.forEach((id, ha) -> {
try { try {
new HostingAssetEntitySaveProcessor(em, ha) new HostingAssetEntitySaveProcessor(em, ha)
.preprocessEntity() .preprocessEntity()
.validateEntity(); .validateEntity()
.prepareForSave();
} catch (final Exception exc) { } catch (final Exception exc) {
System.err.println("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage()); errors.add("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage());
} }
}); });
} }
@Test
@Order(18999)
@ContinueOnFailure
void logValidationErrors() {
this.logErrors();
}
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@Test @Test
@ -393,35 +405,52 @@ public class ImportHostingAssets extends ImportOfficeData {
void verifyPersistedUnixUsersWithUserId() { void verifyPersistedUnixUsersWithUserId() {
assumeThatWeAreImportingControlledTestData(); assumeThatWeAreImportingControlledTestData();
// no contacts yet => mostly null values // no contacts yet => mostly null value
assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace(""" assertThat(firstOfEachType(15, UNIX_USER)).isEqualToIgnoringWhitespace("""
{ {
4005803=HsHostingAssetEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000000}), 4005803=HsHostingAssetRawEntity(UNIX_USER, lug00, LUGs, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102090}),
4005805=HsHostingAssetEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000001}), 4005805=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.1, Paul Klemm, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102091}),
4005809=HsHostingAssetEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000002}), 4005809=HsHostingAssetRawEntity(UNIX_USER, lug00-wla.2, Walter Müller, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 8, "SSD soft quota": 4, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102093}),
4005811=HsHostingAssetEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000003}), 4005811=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.a, LUG OLA - POP a, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102094}),
4005813=HsHostingAssetEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000004}), 4005813=HsHostingAssetRawEntity(UNIX_USER, lug00-ola.b, LUG OLA - POP b, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102095}),
4005835=HsHostingAssetEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 100000005}), 4005835=HsHostingAssetRawEntity(UNIX_USER, lug00-test, Test, MANAGED_WEBSPACE:lug00, { "SSD hard quota": 1024, "SSD soft quota": 1024, "locked": false, "password": null, "shell": "/usr/bin/passwd", "userid": 102106}),
4005964=HsHostingAssetEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000006}), 4005964=HsHostingAssetRawEntity(UNIX_USER, mim00, Michael Mellis, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102147}),
4005966=HsHostingAssetEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000007}), 4005966=HsHostingAssetRawEntity(UNIX_USER, mim00-1981, Jahrgangstreffen 1981, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 256, "SSD soft quota": 128, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102148}),
4005990=HsHostingAssetEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000008}), 4005990=HsHostingAssetRawEntity(UNIX_USER, mim00-mail, Mailbox, MANAGED_WEBSPACE:mim00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 102160}),
4100705=HsHostingAssetEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 100000009}), 4100705=HsHostingAssetRawEntity(UNIX_USER, hsh00-mim, Michael Mellis, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 10003}),
4100824=HsHostingAssetEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000010}), 4100824=HsHostingAssetRawEntity(UNIX_USER, hsh00, Hostsharing Paket, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 10000}),
4167846=HsHostingAssetEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 100000011}), 4167846=HsHostingAssetRawEntity(UNIX_USER, hsh00-dph, hsh00-uph, MANAGED_WEBSPACE:hsh00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/false", "userid": 110568}),
4169546=HsHostingAssetEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000012}), 4169546=HsHostingAssetRawEntity(UNIX_USER, dph00, Reinhard Wiese, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110593}),
4169596=HsHostingAssetEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "HDD hard quota": 0, "HDD soft quota": 0, "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 100000013}) 4169596=HsHostingAssetRawEntity(UNIX_USER, dph00-uph, Domain admin, MANAGED_WEBSPACE:dph00, { "SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "password": null, "shell": "/bin/bash", "userid": 110594})
} }
"""); """);
} }
// ============================================================================================ // ============================================================================================
@Test @Test
@Order(99999) @Order(99999)
void logErrors() { void logErrors() {
if (isImportingControlledTestData()) {
super.expectErrors("""
validation failed for id:5002452( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-empty, mim00-empty, MANAGED_WEBSPACE:mim00, {
"target": "[]"
}
)): ['EMAIL_ALIAS:mim00-empty.config.target' length is expected to be at min 1 but length of [[]] is 0]""",
"""
validation failed for id:5002453( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-0_entries, mim00-0_entries, MANAGED_WEBSPACE:mim00, {
"target": "[]"
}
)): ['EMAIL_ALIAS:mim00-0_entries.config.target' length is expected to be at min 1 but length of [[]] is 0]""",
"""
validation failed for id:5002454( HsHostingAssetRawEntity(EMAIL_ALIAS, mim00-dev.null, mim00-dev.null, MANAGED_WEBSPACE:mim00, {
"target": "[/dev/null]"
})): ['EMAIL_ALIAS:mim00-dev.null.config.target' is expected to match any of [^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9][a-z0-9\\._-]*)?$,^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$,^:include:/.*$,^\\|.*$] but '/dev/null' does not match any]"""
);
} else {
super.logErrors(); super.logErrors();
} }
}
private void persistRecursively(final Integer key, final HsBookingItemEntity bi) { private void persistRecursively(final Integer key, final HsBookingItemEntity bi) {
if (bi.getParentItem() != null) { if (bi.getParentItem() != null) {
@ -434,12 +463,12 @@ public class ImportHostingAssets extends ImportOfficeData {
private void persistHostingAssetsOfType(final HsHostingAssetType hsHostingAssetType) { private void persistHostingAssetsOfType(final HsHostingAssetType hsHostingAssetType) {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context(rbacSuperuser);
hostingAssets.forEach((key, ha) -> { hostingAssets.forEach((key, ha) -> {
context(rbacSuperuser);
if (ha.getType() == hsHostingAssetType) { if (ha.getType() == hsHostingAssetType) {
new HostingAssetEntitySaveProcessor(em, ha) new HostingAssetEntitySaveProcessor(em, ha)
.preprocessEntity() .preprocessEntity()
.validateEntity() .validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*")
.prepareForSave() .prepareForSave()
.saveUsing(entity -> persist(key, entity)) .saveUsing(entity -> persist(key, entity))
.validateContext(); .validateContext();
@ -455,7 +484,7 @@ public class ImportHostingAssets extends ImportOfficeData {
.map(this::trimAll) .map(this::trimAll)
.map(row -> new Record(columns, row)) .map(row -> new Record(columns, row))
.forEach(rec -> { .forEach(rec -> {
final var ipNumber = HsHostingAssetEntity.builder() final var ipNumber = HsHostingAssetRawEntity.builder()
.type(IPV4_NUMBER) .type(IPV4_NUMBER)
.identifier(rec.getString("inet_addr")) .identifier(rec.getString("inet_addr"))
.caption(rec.getString("description")) .caption(rec.getString("description"))
@ -519,9 +548,9 @@ public class ImportHostingAssets extends ImportOfficeData {
+ packet_name) + packet_name)
.isTrue()); .isTrue());
final var asset = HsHostingAssetEntity.builder() final var asset = HsHostingAssetRawEntity.builder()
.isLoaded(haType // this turns off identifier validation to accept former default prefixes
== MANAGED_WEBSPACE) // this turns off identifier validation to accept former default prefixes .isLoaded(haType == MANAGED_WEBSPACE)
.type(haType) .type(haType)
.identifier(packet_name) .identifier(packet_name)
.bookingItem(bookingItem) .bookingItem(bookingItem)
@ -659,11 +688,12 @@ public class ImportHostingAssets extends ImportOfficeData {
.forEach(rec -> { .forEach(rec -> {
final var unixuser_id = rec.getInteger("unixuser_id"); final var unixuser_id = rec.getInteger("unixuser_id");
final var packet_id = rec.getInteger("packet_id"); final var packet_id = rec.getInteger("packet_id");
final var unixUserAsset = HsHostingAssetEntity.builder() final var unixUserAsset = HsHostingAssetRawEntity.builder()
.type(UNIX_USER) .type(UNIX_USER)
.parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id))
.identifier(rec.getString("name")) .identifier(rec.getString("name"))
.caption(rec.getString("comment")) .caption(rec.getString("comment"))
.isLoaded(true) // avoid overwriting imported userids with generated ids
.config(new HashMap<>(Map.ofEntries( .config(new HashMap<>(Map.ofEntries(
entry("shell", rec.getString("shell")), entry("shell", rec.getString("shell")),
// entry("homedir", rec.getString("homedir")), do not import, it's calculated // entry("homedir", rec.getString("homedir")), do not import, it's calculated
@ -675,6 +705,33 @@ public class ImportHostingAssets extends ImportOfficeData {
entry("HDD hard quota", rec.getInteger("storage_hardlimit")) entry("HDD hard quota", rec.getInteger("storage_hardlimit"))
))) )))
.build(); .build();
// TODO.spec: crop SSD+HDD limits if > booked
if (unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0)
> 1024*unixUserAsset.getContextValue("SSD", Integer.class, 0)) {
unixUserAsset.getConfig().put("SSD hard quota", unixUserAsset.getContextValue("SSD", Integer.class, 0)*1024);
}
if (unixUserAsset.getDirectValue("HDD hard quota", Integer.class, 0)
> 1024*unixUserAsset.getContextValue("HDD", Integer.class, 0)) {
unixUserAsset.getConfig().put("HDD hard quota", unixUserAsset.getContextValue("HDD", Integer.class, 0)*1024);
}
// TODO.spec: does `softlimit<hardlimit?` even make sense? Fix it in this or the other direction?
if (unixUserAsset.getDirectValue("SSD soft quota", Integer.class, 0)
> unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0)) {
unixUserAsset.getConfig().put("SSD soft quota", unixUserAsset.getConfig().get("SSD hard quota"));
}
if (unixUserAsset.getDirectValue("HDD soft quota", Integer.class, 0)
> unixUserAsset.getDirectValue("HDD hard quota", Integer.class, 0)) {
unixUserAsset.getConfig().put("HDD soft quota", unixUserAsset.getConfig().get("HDD hard quota"));
}
// TODO.spec: remove HDD limits if no HDD storage is booked
if (unixUserAsset.getContextValue("HDD", Integer.class, 0) == 0) {
unixUserAsset.getConfig().remove("HDD hard quota");
unixUserAsset.getConfig().remove("HDD soft quota");
}
hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset);
}); });
} }
@ -688,7 +745,7 @@ public class ImportHostingAssets extends ImportOfficeData {
final var unixuser_id = rec.getInteger("emailalias_id"); final var unixuser_id = rec.getInteger("emailalias_id");
final var packet_id = rec.getInteger("pac_id"); final var packet_id = rec.getInteger("pac_id");
final var targets = parseCsvLine(rec.getString("target")); final var targets = parseCsvLine(rec.getString("target"));
final var unixUserAsset = HsHostingAssetEntity.builder() final var unixUserAsset = HsHostingAssetRawEntity.builder()
.type(EMAIL_ALIAS) .type(EMAIL_ALIAS)
.parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id)) .parentAsset(hostingAssets.get(PACKET_ID_OFFSET + packet_id))
.identifier(rec.getString("name")) .identifier(rec.getString("name"))
@ -697,7 +754,7 @@ public class ImportHostingAssets extends ImportOfficeData {
entry("target", targets) entry("target", targets)
)) ))
.build(); .build();
hostingAssets.put(UNIXUSER_ID_OFFSET + unixuser_id, unixUserAsset); hostingAssets.put(EMAILALIAS_ID_OFFSET + unixuser_id, unixUserAsset);
}); });
} }
@ -730,7 +787,7 @@ public class ImportHostingAssets extends ImportOfficeData {
}; };
} }
private static HsHostingAssetEntity ipNumber(final Integer inet_addr_id) { private static HsHostingAssetRawEntity ipNumber(final Integer inet_addr_id) {
return inet_addr_id != null ? hostingAssets.get(IP_NUMBER_ID_OFFSET + inet_addr_id) : null; return inet_addr_id != null ? hostingAssets.get(IP_NUMBER_ID_OFFSET + inet_addr_id) : null;
} }
@ -738,7 +795,7 @@ public class ImportHostingAssets extends ImportOfficeData {
return hive_id != null ? hives.get(HIVE_ID_OFFSET + hive_id) : null; return hive_id != null ? hives.get(HIVE_ID_OFFSET + hive_id) : null;
} }
private static HsHostingAssetEntity pac(final Integer packet_id) { private static HsHostingAssetRawEntity pac(final Integer packet_id) {
return packet_id != null ? hostingAssets.get(PACKET_ID_OFFSET + packet_id) : null; return packet_id != null ? hostingAssets.get(PACKET_ID_OFFSET + packet_id) : null;
} }

View File

@ -103,6 +103,11 @@ class PasswordPropertyUnitTest {
// when // when
final var result = passwordProp.compute(em, new PropertiesProvider() { final var result = passwordProp.compute(em, new PropertiesProvider() {
@Override
public boolean isLoaded() {
return false;
}
@Override @Override
public Map<String, Object> directProps() { public Map<String, Object> directProps() {
return Map.ofEntries( return Map.ofEntries(

View File

@ -5,3 +5,7 @@ emailalias_id;pac_id;name;target
2431;1112;mim00-abruf;michael.mellis@hostsharing.net 2431;1112;mim00-abruf;michael.mellis@hostsharing.net
2449;1112;mim00-hhfx;"mim00-hhfx,""|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l""" 2449;1112;mim00-hhfx;"mim00-hhfx,""|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l"""
2451;1112;mim00-hhfx-l;:include:/home/pacs/mim00/etc/hhfx.list 2451;1112;mim00-hhfx-l;:include:/home/pacs/mim00/etc/hhfx.list
2452;1112;mim00-empty;
2453;1112;mim00-0_entries;""
2454;1112;mim00-dev.null; /dev/null
2455;1112;mim00-1_with_space;" ""|/home/pacs/mim00/install/corpslistar/listar"""

1 emailalias_id pac_id name target
5 2431 1112 mim00-abruf michael.mellis@hostsharing.net
6 2449 1112 mim00-hhfx mim00-hhfx,"|/usr/bin/formail -I 'Reply-To: hamburger-fx@example.net' | /usr/lib/sendmail mim00-hhfx-l"
7 2451 1112 mim00-hhfx-l :include:/home/pacs/mim00/etc/hhfx.list
8 2452 1112 mim00-empty
9 2453 1112 mim00-0_entries
10 2454 1112 mim00-dev.null /dev/null
11 2455 1112 mim00-1_with_space "|/home/pacs/mim00/install/corpslistar/listar"

View File

@ -2,11 +2,11 @@ unixuser_id;name;comment;shell;homedir;locked;packet_id;userid;quota_softlimit;q
100824;hsh00;Hostsharing Paket;/bin/bash;/home/pacs/hsh00;0;630;10000;0;0;0;0 100824;hsh00;Hostsharing Paket;/bin/bash;/home/pacs/hsh00;0;630;10000;0;0;0;0
5803;lug00;LUGs;/bin/bash;/home/pacs/lug00;0;1094;102090;0;0;0;0 5803;lug00;LUGs;/bin/bash;/home/pacs/lug00;0;1094;102090;0;0;0;0
5805;lug00-wla.1;Paul Klemm;/bin/bash;/home/pacs/lug00/users/deaf;0;1094;102091;0;0;0;0 5805;lug00-wla.1;Paul Klemm;/bin/bash;/home/pacs/lug00/users/deaf;0;1094;102091;4;0;0;0
5809;lug00-wla.2;Walter Müller;/bin/bash;/home/pacs/lug00/users/marl;0;1094;102093;0;0;0;0 5809;lug00-wla.2;Walter Müller;/bin/bash;/home/pacs/lug00/users/marl;0;1094;102093;4;8;0;0
5811;lug00-ola.a;LUG OLA - POP a;/usr/bin/passwd;/home/pacs/lug00/users/marl.a;1;1094;102094;0;0;0;0 5811;lug00-ola.a;LUG OLA - POP a;/usr/bin/passwd;/home/pacs/lug00/users/marl.a;1;1094;102094;0;0;0;0
5813;lug00-ola.b;LUG OLA - POP b;/usr/bin/passwd;/home/pacs/lug00/users/marl.b;1;1094;102095;0;0;0;0 5813;lug00-ola.b;LUG OLA - POP b;/usr/bin/passwd;/home/pacs/lug00/users/marl.b;1;1094;102095;0;0;0;0
5835;lug00-test;Test;/usr/bin/passwd;/home/pacs/lug00/users/test;0;1094;102106;0;0;0;0 5835;lug00-test;Test;/usr/bin/passwd;/home/pacs/lug00/users/test;0;1094;102106;2000000;4000000;20;0
100705;hsh00-mim;Michael Mellis;/bin/false;/home/pacs/hsh00/users/mi;0;630;10003;0;0;0;0 100705;hsh00-mim;Michael Mellis;/bin/false;/home/pacs/hsh00/users/mi;0;630;10003;0;0;0;0
5964;mim00;Michael Mellis;/bin/bash;/home/pacs/mim00;0;1112;102147;0;0;0;0 5964;mim00;Michael Mellis;/bin/bash;/home/pacs/mim00;0;1112;102147;0;0;0;0

1 unixuser_id name comment shell homedir locked packet_id userid quota_softlimit quota_hardlimit storage_softlimit storage_hardlimit
2 100824 hsh00 Hostsharing Paket /bin/bash /home/pacs/hsh00 0 630 10000 0 0 0 0
3 5803 lug00 LUGs /bin/bash /home/pacs/lug00 0 1094 102090 0 0 0 0
4 5805 lug00-wla.1 Paul Klemm /bin/bash /home/pacs/lug00/users/deaf 0 1094 102091 0 4 0 0 0
5 5809 lug00-wla.2 Walter Müller /bin/bash /home/pacs/lug00/users/marl 0 1094 102093 0 4 0 8 0 0
6 5811 lug00-ola.a LUG OLA - POP a /usr/bin/passwd /home/pacs/lug00/users/marl.a 1 1094 102094 0 0 0 0
7 5813 lug00-ola.b LUG OLA - POP b /usr/bin/passwd /home/pacs/lug00/users/marl.b 1 1094 102095 0 0 0 0
8 5835 lug00-test Test /usr/bin/passwd /home/pacs/lug00/users/test 0 1094 102106 0 2000000 0 4000000 0 20 0
9 100705 hsh00-mim Michael Mellis /bin/false /home/pacs/hsh00/users/mi 0 630 10003 0 0 0 0
10 5964 mim00 Michael Mellis /bin/bash /home/pacs/mim00 0 1112 102147 0 0 0 0
11 5966 mim00-1981 Jahrgangstreffen 1981 /bin/bash /home/pacs/mim00/users/1981 0 1112 102148 128 256 0 0
12 5990 mim00-mail Mailbox /bin/bash /home/pacs/mim00/users/mail 0 1112 102160 0 0 0 0