Skip to content

Commit

Permalink
cleanup & validation: moved cleanup processor (validation part) to Ob…
Browse files Browse the repository at this point in the history
…jectValidator
  • Loading branch information
1azyman committed Apr 15, 2024
1 parent 9ee3b24 commit 047bd7a
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ObjectUpgradeValidator {
private final ObjectValidator validator;

public ObjectUpgradeValidator(@NotNull PrismContext prismContext) {
this.validator = new ObjectValidator(prismContext);
this.validator = new ObjectValidator();
}

public void setWarnDeprecated(boolean value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@
*/
package com.evolveum.midpoint.schema.validator;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.*;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.LocalizableMessage;
import com.evolveum.midpoint.util.SingleLocalizableMessage;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;

/**
* Validator that can process objects, validate them, check for errors and warning
Expand All @@ -40,68 +41,77 @@ public class ObjectValidator {

private static final Trace LOGGER = TraceManager.getTrace(ObjectValidator.class);

private final PrismContext prismContext;
private boolean warnDeprecated = false;
private boolean warnRemoved = false;
private boolean warnPlannedRemoval = false;
private Set<ValidationItemType> typesToCheck = new HashSet<>();

private String warnPlannedRemovalVersion = null;
private boolean warnIncorrectOid = false;

public ObjectValidator(PrismContext prismContext) {
super();
this.prismContext = prismContext;
private boolean summarizeItemLifecycleState = true;

public Set<ValidationItemType> getTypesToCheck() {
return typesToCheck;
}

public boolean isWarnDeprecated() {
return warnDeprecated;
public void setTypesToCheck(@NotNull Set<ValidationItemType> typesToCheck) {
this.typesToCheck = typesToCheck;
}

public void setWarnDeprecated(boolean warnDeprecated) {
this.warnDeprecated = warnDeprecated;
public String getWarnPlannedRemovalVersion() {
return warnPlannedRemovalVersion;
}

public boolean isWarnPlannedRemoval() {
return warnPlannedRemoval;
public void setWarnPlannedRemovalVersion(String warnPlannedRemovalVersion) {
this.warnPlannedRemovalVersion = warnPlannedRemovalVersion;
}

public void setWarnPlannedRemoval(boolean warnPlannedRemoval) {
this.warnPlannedRemoval = warnPlannedRemoval;
public boolean isSummarizeItemLifecycleState() {
return summarizeItemLifecycleState;
}

public String getWarnPlannedRemovalVersion() {
return warnPlannedRemovalVersion;
public void setSummarizeItemLifecycleState(boolean summarizeItemLifecycleState) {
this.summarizeItemLifecycleState = summarizeItemLifecycleState;
}

public void setWarnPlannedRemovalVersion(String warnPlannedRemovalVersion) {
this.warnPlannedRemovalVersion = warnPlannedRemovalVersion;
@Deprecated
public void setWarnDeprecated(boolean warnDeprecated) {
setTypeToCheck(ValidationItemType.DEPRECATED_ITEM, warnDeprecated);
}

public void setWarnIncorrectOids(boolean value) {
this.warnIncorrectOid = value;
@Deprecated
public void setWarnPlannedRemoval(boolean warnPlannedRemoval) {
setTypeToCheck(ValidationItemType.PLANNED_REMOVAL_ITEM, warnPlannedRemoval);
}

public boolean isWarnRemoved() {
return warnRemoved;
@Deprecated
public void setWarnIncorrectOids(boolean value) {
setTypeToCheck(ValidationItemType.INCORRECT_OID_FORMAT, value);
}

@Deprecated
public void setWarnRemoved(boolean warnRemoved) {
this.warnRemoved = warnRemoved;
setTypeToCheck(ValidationItemType.REMOVED_ITEM, warnRemoved);
}

@Deprecated
private void setTypeToCheck(ValidationItemType type, boolean set) {
if (set) {
typesToCheck.add(type);
} else {
typesToCheck.remove(type);
}
}

public void setAllWarnings() {
this.warnDeprecated = true;
this.warnPlannedRemoval = true;
this.warnIncorrectOid = true;
this.warnRemoved = true;
typesToCheck.addAll(Arrays.asList(ValidationItemType.values()));
}

public <O extends Objectable> ValidationResult validate(PrismObject<O> object) {
ValidationResult result = new ValidationResult();
object.accept(visitable -> visit(visitable, result));

return result;
}

private void visit(Visitable visitable, ValidationResult result) {
private void visit(Visitable<?> visitable, ValidationResult result) {
if (visitable instanceof Item<?, ?>) {
visitItem((Item<?, ?>) visitable, result);
} else if (visitable instanceof PrismValue) {
Expand All @@ -110,7 +120,7 @@ private void visit(Visitable visitable, ValidationResult result) {
}

private void visitValue(PrismValue value, ValidationResult result) {
if (warnIncorrectOid) {
if (check(ValidationItemType.INCORRECT_OID_FORMAT)) {
if (value instanceof PrismObjectValue<?>) {
checkOid(result, value, ((PrismObjectValue<?>) value).getOid());
} else if (value instanceof PrismReferenceValue) {
Expand All @@ -119,6 +129,10 @@ private void visitValue(PrismValue value, ValidationResult result) {
}
}

private boolean check(ValidationItemType type) {
return typesToCheck.contains(type);
}

private <V extends PrismValue, D extends ItemDefinition<?>> void visitItem(Item<V, D> item, ValidationResult result) {
if (item.isRaw()) {
return;
Expand All @@ -130,28 +144,152 @@ private <V extends PrismValue, D extends ItemDefinition<?>> void visitItem(Item<
}

List<String> messages = new ArrayList<>();
if (warnDeprecated && definition.isDeprecated()) {
messages.add("deprecated");
if (check(ValidationItemType.DEPRECATED_ITEM) && definition.isDeprecated()) {
if (!summarizeItemLifecycleState) {
warn(
result, ValidationItemType.DEPRECATED_ITEM, "Deprecated item " + item.getElementName().getLocalPart(),
item, item);
} else {
messages.add("deprecated");
}
}

if (warnPlannedRemoval) {
if (check(ValidationItemType.PLANNED_REMOVAL_ITEM)) {
String plannedRemoval = definition.getPlannedRemoval();
if (plannedRemoval != null) {
if (warnPlannedRemovalVersion == null || plannedRemoval.equals(warnPlannedRemovalVersion)) {
messages.add("planned for removal in version " + plannedRemoval);
if (!summarizeItemLifecycleState) {
warn(
result, ValidationItemType.PLANNED_REMOVAL_ITEM,
"Item " + item.getElementName().getLocalPart() + " planned for removal in version " + plannedRemoval,
item, item);
} else {
messages.add("planned for removal in version " + plannedRemoval);
}
}
}
}

if (warnRemoved && definition.isRemoved()) {
messages.add("removed");
if (check(ValidationItemType.REMOVED_ITEM) && definition.isRemoved()) {
if (!summarizeItemLifecycleState) {
warn(
result, ValidationItemType.REMOVED_ITEM, "Removed item " + item.getElementName().getLocalPart(),
item, item);
} else {
messages.add("removed");
}
}

if (summarizeItemLifecycleState && !messages.isEmpty()) {
warn(
result, ValidationItemType.DEPRECATED_REMOVED_PLANNED_REMOVAL_ITEM,
StringUtils.join(messages, ", "), item, item);
}

if (item instanceof PrismProperty<?> property) {
visitProperty(property, result);
} else if (item instanceof PrismReference) {
visitReference((PrismReference) item, result);
} else if (item instanceof PrismContainer<?> container) {
visitContainer(container, result);
}
}

private void visitContainer(PrismContainer<?> container, ValidationResult result) {
PrismContainerDefinition<?> def = container.getDefinition();
if (def == null) {
return;
}

if (!def.isMultiValue()) {
return;
}

// todo enable natural keys check
// List<QName> constituents = def.getNaturalKey();
// if (constituents == null || constituents.isEmpty()) {
// return;
// }
//
// for (PrismContainerValue<?> value : container.getValues()) {
// for (QName key : constituents) {
// if (value.findItem(ItemPath.create(key)) == null) {
// warn(
// result, ValidationItemType.MISSING_NATURAL_KEY,
// "Missing natural key constituent: " + key.getLocalPart(), container, value);
// }
// }
// }
}

private void visitProperty(PrismProperty<?> property, ValidationResult result) {
if (check(ValidationItemType.PROTECTED_DATA_NOT_EXTERNAL) && ProtectedStringType.COMPLEX_TYPE.equals(property.getDefinition().getTypeName())) {
PrismPropertyDefinition<?> def = property.getDefinition();
Class<?> type = def.getTypeClass();
if (ProtectedDataType.class.isAssignableFrom(type)) {
checkProtectedString(property, result);
}
}
}

private void checkProtectedString(PrismProperty<?> property, ValidationResult result) {
ProtectedStringViolations violations = new ProtectedStringViolations();

List<String> messages = new ArrayList<>();
for (PrismPropertyValue<?> value : property.getValues()) {
ProtectedDataType<?> ps = (ProtectedDataType<?>) value.getValue();
if (ps == null) {
continue;
}

if (ps.getEncryptedDataType() != null) {
messages.add("encrypted data in " + property.getPath());
violations.addEncrypted(property.getPath());
}

if (ps.getHashedDataType() != null) {
messages.add("hashed data in " + property.getPath());
violations.addHashed(property.getPath());
}

if (ps.getClearValue() != null) {
messages.add("clear value in " + property.getPath());
violations.addClearValue(property.getPath());
}
}

if (messages.isEmpty()) {
return;
}

warn(result, item, StringUtils.join(messages, ", "));
warn(
result, ValidationItemType.PROTECTED_DATA_NOT_EXTERNAL,
"Protected string: " + StringUtils.join(messages, ", "), property, violations);
}

private void visitReference(PrismReference reference, ValidationResult result) {
if (check(ValidationItemType.MULTIVALUE_REF_WITHOUT_OID)) {
checkMultiValueReference(reference, result);
}
}

private void checkMultiValueReference(PrismReference reference, ValidationResult result) {
PrismReferenceDefinition def = reference.getDefinition();
if (def == null || def.isSingleValue()) {
return;
}

List<PrismReferenceValue> missingOids = reference.getValues().stream()
.filter(v -> StringUtils.isEmpty(v.getOid()))
.toList();

ItemPath path = reference.getPath();

for (PrismReferenceValue value : missingOids) {
warn(
result, ValidationItemType.MULTIVALUE_REF_WITHOUT_OID,
String.format("Multi-value reference without oid on path %s", path), reference, value);
}
}

private void checkOid(ValidationResult result, PrismValue item, String oid) {
Expand All @@ -161,22 +299,22 @@ private void checkOid(ValidationResult result, PrismValue item, String oid) {
try {
UUID.fromString(oid);
} catch (IllegalArgumentException e) {
warn(result, (Item<?, ?>) item.getParent(), "OID '" + oid + "' is not valid UUID");
warn(result, ValidationItemType.INCORRECT_OID_FORMAT, "OID '" + oid + "' is not valid UUID", (Item<?, ?>) item.getParent(), item);
}
}

private <V extends PrismValue, D extends ItemDefinition<?>> void warn(ValidationResult result, Item<V, D> item, String message) {
msg(result, OperationResultStatus.WARNING, item, message);
private <V extends PrismValue, D extends ItemDefinition<?>> void warn(
ValidationResult result, ValidationItemType type, String message, Item<V, D> item, Object data) {
msg(result, type, ValidationItemStatus.WARNING, message, item, data);
}

private <V extends PrismValue, D extends ItemDefinition<?>> void msg(ValidationResult result, OperationResultStatus status, Item<V, D> item, String message) {
ValidationItem resultItem = new ValidationItem();
resultItem.setStatus(status);
if (item != null) {
resultItem.setItemPath(item.getPath());
}
LocalizableMessage lMessage = new SingleLocalizableMessage(null, null, message);
resultItem.setMessage(lMessage);
result.addItem(resultItem);
private <V extends PrismValue, D extends ItemDefinition<?>> void msg(
ValidationResult result, ValidationItemType type, ValidationItemStatus status, String message, Item<V, D> item,
Object data) {

ItemPath path = item != null ? item.getPath() : null;
LocalizableMessage msg = new SingleLocalizableMessage(null, null, message);

result.addItem(new ValidationItem<>(type, status, msg, path, data));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.evolveum.midpoint.schema.validator;

public class ObjectValidatorListener {
}

0 comments on commit 047bd7a

Please sign in to comment.