diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java
index abc5198ec29..8f75e49bf88 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java
@@ -256,7 +256,7 @@ DD findItemDelta(ItemPath propertyPath, Class
deltaType, Class itemType)
}
}
- public PartiallyResolvedDelta findPartial(ItemPath propertyPath) {
+ public Collection> findPartial(ItemPath propertyPath) {
if (changeType == ChangeType.ADD) {
PartiallyResolvedItem partialValue = objectToAdd.findPartial(propertyPath);
if (partialValue == null || partialValue.getItem() == null) {
@@ -265,20 +265,23 @@ public PartiallyResolvedDelta<
Item item = partialValue.getItem();
ItemDelta itemDelta = item.createDelta();
itemDelta.addValuesToAdd(item.getClonedValues());
- return new PartiallyResolvedDelta(itemDelta, partialValue.getResidualPath());
+ Collection> deltas = new ArrayList<>(1);
+ deltas.add(new PartiallyResolvedDelta(itemDelta, partialValue.getResidualPath()));
+ return deltas;
} else if (changeType == ChangeType.MODIFY) {
+ Collection> deltas = new ArrayList<>();
for (ItemDelta,?> modification: modifications) {
CompareResult compareComplex = modification.getPath().compareComplex(propertyPath);
if (compareComplex == CompareResult.EQUIVALENT) {
- return new PartiallyResolvedDelta((ItemDelta)modification, null);
- } else if (compareComplex == CompareResult.SUPERPATH) {
- return new PartiallyResolvedDelta((ItemDelta)modification, null);
+ deltas.add(new PartiallyResolvedDelta((ItemDelta)modification, null));
} else if (compareComplex == CompareResult.SUBPATH) {
- return new PartiallyResolvedDelta((ItemDelta)modification,
- propertyPath.remainder(modification.getPath()));
+ deltas.add(new PartiallyResolvedDelta((ItemDelta)modification, null));
+ } else if (compareComplex == CompareResult.SUPERPATH) {
+ deltas.add(new PartiallyResolvedDelta((ItemDelta)modification,
+ modification.getPath().remainder(propertyPath)));
}
}
- return null;
+ return deltas;
} else {
return null;
}
diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/PartiallyResolvedDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/PartiallyResolvedDelta.java
index 08d3aa2562f..a2c2b0222b3 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/PartiallyResolvedDelta.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/PartiallyResolvedDelta.java
@@ -18,12 +18,14 @@
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.path.ItemPath;
+import com.evolveum.midpoint.util.DebugDumpable;
+import com.evolveum.midpoint.util.DebugUtil;
/**
* @author semancik
*
*/
-public class PartiallyResolvedDelta {
+public class PartiallyResolvedDelta implements DebugDumpable {
private ItemDelta delta;
private ItemPath residualPath;
@@ -80,6 +82,22 @@ public boolean equals(Object obj) {
return false;
return true;
}
+
+ @Override
+ public String debugDump() {
+ return debugDump(0);
+ }
+
+ @Override
+ public String debugDump(int indent) {
+ StringBuilder sb = new StringBuilder();
+ DebugUtil.indentDebugDump(sb, indent);
+ sb.append("PartiallyResolvedDelta\n");
+ DebugUtil.debugDumpWithLabel(sb, "delta", delta, indent + 1);
+ sb.append("\n");
+ DebugUtil.debugDumpWithLabel(sb, "residual path", residualPath==null?"null":residualPath.toString(), indent + 1);
+ return sb.toString();
+ }
@Override
public String toString() {
diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java
index c3022fb0d3c..b3e49022575 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/path/ItemPath.java
@@ -424,6 +424,28 @@ public static IdItemPathSegment getFirstIdSegment(ItemPath itemPath) {
return null;
}
+ public static NameItemPathSegment getFirstNameSegment(ItemPath itemPath) {
+ if (itemPath == null) {
+ return null;
+ }
+ ItemPathSegment first = itemPath.first();
+ if (first instanceof NameItemPathSegment) {
+ return (NameItemPathSegment)first;
+ }
+ if (first instanceof IdItemPathSegment) {
+ return getFirstNameSegment(itemPath.rest());
+ }
+ return null;
+ }
+
+ public static QName getFirstName(ItemPath itemPath) {
+ NameItemPathSegment nameSegment = getFirstNameSegment(itemPath);
+ if (nameSegment == null) {
+ return null;
+ }
+ return nameSegment.getName();
+ }
+
public static ItemPath pathRestStartingWithName(ItemPath path) {
ItemPathSegment pathSegment = path.first();
if (pathSegment instanceof NameItemPathSegment) {
@@ -435,6 +457,15 @@ public static ItemPath pathRestStartingWithName(ItemPath path) {
}
}
+ public boolean containsName(QName name) {
+ for (ItemPathSegment segment: segments) {
+ if (segment instanceof NameItemPathSegment && ((NameItemPathSegment)segment).getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-3.xsd
index 41e05801b97..b50aaf1ac83 100644
--- a/infra/schema/src/main/resources/xml/ns/public/common/common-3.xsd
+++ b/infra/schema/src/main/resources/xml/ns/public/common/common-3.xsd
@@ -2406,6 +2406,16 @@
+
+
+
+ Timestamps and general metadata describing the credential change.
+
+
+ true
+
+
+
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java
index 20b68dee048..4fc2604e521 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java
@@ -858,7 +858,7 @@ void logDeltaExecution(ObjectDelta objectDelta, LensContext context,
sb.append(" delta of ").append(objectDelta.getObjectTypeClass().getSimpleName());
sb.append(" ]---------------------\n");
DebugUtil.debugDumpLabel(sb, "Channel", 0);
- sb.append(" ").append(getChannel(context, task)).append("\n");
+ sb.append(" ").append(LensUtil.getChannel(context, task)).append("\n");
if (context != null) {
DebugUtil.debugDumpLabel(sb, "Wave", 0);
sb.append(" ").append(context.getExecutionWave()).append("\n");
@@ -1041,13 +1041,7 @@ private void executeModification(Ob
}
private void applyMetadata(LensContext context, Task task, T objectTypeToAdd, OperationResult result) throws SchemaException {
- MetadataType metaData = new MetadataType();
- String channel = getChannel(context, task);
- metaData.setCreateChannel(channel);
- metaData.setCreateTimestamp(clock.currentTimeXMLGregorianCalendar());
- if (task.getOwner() != null) {
- metaData.setCreatorRef(ObjectTypeUtil.createObjectRef(task.getOwner()));
- }
+ MetadataType metaData = LensUtil.createCreateMetadata(context, clock.currentTimeXMLGregorianCalendar(), task);
if (workflowManager != null) {
metaData.getCreateApproverRef().addAll(workflowManager.getApprovedBy(task, result));
}
@@ -1055,33 +1049,16 @@ private void applyMetadata(LensCont
objectTypeToAdd.setMetadata(metaData);
}
- private String getChannel(LensContext context, Task task){
- if (context != null && context.getChannel() != null){
- return context.getChannel();
- } else if (task.getChannel() != null){
- return task.getChannel();
- }
- return null;
- }
+
private void applyMetadata(ObjectDelta change, LensElementContext objectContext,
Class objectTypeClass, Task task, LensContext context, OperationResult result) throws SchemaException {
- String channel = getChannel(context, task);
-
- PrismObjectDefinition def = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(objectTypeClass);
+ String channel = LensUtil.getChannel(context, task);
- if (channel != null) {
- PropertyDelta delta = PropertyDelta.createModificationReplaceProperty((new ItemPath(ObjectType.F_METADATA, MetadataType.F_MODIFY_CHANNEL)), def, channel);
- ((Collection) change.getModifications()).add(delta);
- }
- PropertyDelta delta = PropertyDelta.createModificationReplaceProperty((new ItemPath(ObjectType.F_METADATA, MetadataType.F_MODIFY_TIMESTAMP)), def, XmlTypeConverter.createXMLGregorianCalendar(System.currentTimeMillis()));
- ((Collection) change.getModifications()).add(delta);
- if (task.getOwner() != null) {
- ReferenceDelta refDelta = ReferenceDelta.createModificationReplace((new ItemPath(ObjectType.F_METADATA,
- MetadataType.F_MODIFIER_REF)), def, task.getOwner().getOid());
- ((Collection) change.getModifications()).add(refDelta);
- }
+ PrismObjectDefinition def = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(objectTypeClass);
+ ((Collection) change.getModifications()).addAll(LensUtil.createModifyMetadataDeltas(context, new ItemPath(ObjectType.F_METADATA), def, clock.currentTimeXMLGregorianCalendar(), task));
+
List approverReferenceValues = new ArrayList();
if (workflowManager != null) {
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java
index f221f95e5e8..9dc25d97b9a 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java
@@ -55,7 +55,9 @@
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
+import com.evolveum.midpoint.prism.delta.ReferenceDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
+import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.GetOperationOptions;
@@ -63,6 +65,7 @@
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
+import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.DOMUtil;
@@ -87,6 +90,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.LayerType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingStrengthType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectPolicyConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType;
@@ -1281,4 +1285,42 @@ public static AssignmentType getAssignmentType(ItemDeltaItem)assignmentIdi.getItemNew()).getValue(0).asContainerable();
}
}
+
+ public static MetadataType createCreateMetadata(LensContext context, XMLGregorianCalendar now, Task task) {
+ MetadataType metaData = new MetadataType();
+ String channel = getChannel(context, task);
+ metaData.setCreateChannel(channel);
+ metaData.setCreateTimestamp(now);
+ if (task.getOwner() != null) {
+ metaData.setCreatorRef(ObjectTypeUtil.createObjectRef(task.getOwner()));
+ }
+ return metaData;
+ }
+
+ public static Collection extends ItemDelta,?>> createModifyMetadataDeltas(LensContext context,
+ ItemPath metadataPath, PrismObjectDefinition def, XMLGregorianCalendar now, Task task) {
+ Collection extends ItemDelta,?>> deltas = new ArrayList<>();
+ String channel = getChannel(context, task);
+ if (channel != null) {
+ PropertyDelta delta = PropertyDelta.createModificationReplaceProperty(metadataPath.subPath(MetadataType.F_MODIFY_CHANNEL), def, channel);
+ ((Collection)deltas).add(delta);
+ }
+ PropertyDelta delta = PropertyDelta.createModificationReplaceProperty(metadataPath.subPath(MetadataType.F_MODIFY_TIMESTAMP), def, now);
+ ((Collection)deltas).add(delta);
+ if (task.getOwner() != null) {
+ ReferenceDelta refDelta = ReferenceDelta.createModificationReplace(
+ metadataPath.subPath(MetadataType.F_MODIFIER_REF), def, task.getOwner().getOid());
+ ((Collection)deltas).add(refDelta);
+ }
+ return deltas;
+ }
+
+ public static String getChannel(LensContext context, Task task) {
+ if (context != null && context.getChannel() != null){
+ return context.getChannel();
+ } else if (task.getChannel() != null){
+ return task.getChannel();
+ }
+ return null;
+ }
}
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java
index bd5726eadc3..5d26a00898e 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java
@@ -15,6 +15,12 @@
*/
package com.evolveum.midpoint.model.impl.lens.projector;
+import java.util.Collection;
+import java.util.List;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+
import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
import com.evolveum.midpoint.common.refinery.ResourceShadowDiscriminator;
import com.evolveum.midpoint.model.api.PolicyViolationException;
@@ -28,8 +34,12 @@
import com.evolveum.midpoint.model.impl.lens.LensFocusContext;
import com.evolveum.midpoint.model.impl.lens.LensProjectionContext;
import com.evolveum.midpoint.model.impl.lens.LensUtil;
+import com.evolveum.midpoint.prism.Containerable;
+import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.OriginType;
+import com.evolveum.midpoint.prism.PrismContainer;
+import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
@@ -38,9 +48,13 @@
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ChangeType;
+import com.evolveum.midpoint.prism.delta.ContainerDelta;
+import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
+import com.evolveum.midpoint.prism.delta.PartiallyResolvedDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
+import com.evolveum.midpoint.prism.path.ItemPathSegment;
import com.evolveum.midpoint.prism.schema.PrismSchema;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
@@ -76,35 +90,50 @@ public class CredentialsProcessor {
private PrismContext prismContext;
@Autowired(required = true)
- private MappingFactory valueConstructionFactory;
+ private MappingFactory mappingFactory;
@Autowired(required = true)
private PasswordPolicyProcessor passwordPolicyProcessor;
- public void processCredentials(LensContext context, LensProjectionContext projectionContext, Task task, OperationResult result)
+ public void processFocusCredentials(LensContext context,
+ XMLGregorianCalendar now, Task task, OperationResult result)
throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException {
LensFocusContext focusContext = context.getFocusContext();
if (focusContext != null && FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) {
- processCredentialsFocal((LensContext extends FocusType>)context, projectionContext, task, result);
-// return;
+ processFocusPassword((LensContext extends FocusType>)context, now, task, result);
+ }
+ }
+
+ private void processFocusPassword(LensContext context,
+ XMLGregorianCalendar now, Task task, OperationResult result)
+ throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException {
+ LensFocusContext focusContext = context.getFocusContext();
+
+ processFocusCredentialsCommon(context, new ItemPath(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD), now, task, result);
+
+ passwordPolicyProcessor.processPasswordPolicy(focusContext, context, result);
+ }
+
+ public void processProjectionCredentials(LensContext context, LensProjectionContext projectionContext,
+ XMLGregorianCalendar now, Task task, OperationResult result)
+ throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException {
+ LensFocusContext focusContext = context.getFocusContext();
+ if (focusContext != null && FocusType.class.isAssignableFrom(focusContext.getObjectTypeClass())) {
+ processProjectionPassword((LensContext extends FocusType>)context, projectionContext, now, task, result);
}
-// if (focusContext.getObjectTypeClass() != UserType.class) {
-// // We can do this only for user.
-// return;
-// }
passwordPolicyProcessor.processPasswordPolicy(projectionContext, context, result);
}
-
- public void processCredentialsFocal(LensContext context,
- final LensProjectionContext accCtx, Task task, OperationResult result)
+ private void processProjectionPassword(LensContext context,
+ final LensProjectionContext accCtx, XMLGregorianCalendar now, Task task, OperationResult result)
throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
LensFocusContext focusContext = context.getFocusContext();
- ObjectDelta userDelta = focusContext.getDelta();
+ ObjectDelta focusDelta = focusContext.getDelta();
+
PropertyDelta userPasswordValueDelta = null;
- if (userDelta != null) {
- userPasswordValueDelta = userDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE);
+ if (focusDelta != null) {
+ userPasswordValueDelta = focusDelta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE);
}
PrismObject userNew = focusContext.getObjectNew();
@@ -115,7 +144,7 @@ public void processCredentialsFocal(LensContext context
}
PrismObjectDefinition accountDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(ShadowType.class);
- PrismPropertyDefinition accountPasswordPropertyDefinition = accountDefinition.findPropertyDefinition(SchemaConstants.PATH_PASSWORD_VALUE);
+ PrismPropertyDefinition accountPasswordPropertyDefinition = accountDefinition.findPropertyDefinition(SchemaConstants.PATH_PASSWORD_VALUE);
ResourceShadowDiscriminator rat = accCtx.getResourceShadowDiscriminator();
@@ -151,7 +180,7 @@ public void processCredentialsFocal(LensContext context
return;
}
- Mapping,PrismPropertyDefinition> passwordMapping = valueConstructionFactory.createMapping(outboundMappingType,
+ Mapping,PrismPropertyDefinition> passwordMapping = mappingFactory.createMapping(outboundMappingType,
"outbound password mapping in account type " + rat);
if (!passwordMapping.isApplicableToChannel(context.getChannel())) {
return;
@@ -208,5 +237,99 @@ public StringPolicyType resolve() {
}
+ private void processFocusCredentialsCommon(LensContext context,
+ ItemPath credentialsPath, XMLGregorianCalendar now, Task task, OperationResult result)
+ throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
+ LensFocusContext focusContext = context.getFocusContext();
+ if (focusContext.isAdd()) {
+ PrismObject focus = focusContext.getObjectNew();
+ PrismContainer credentialsContainer = focus.findContainer(credentialsPath);
+ if (credentialsContainer != null) {
+ for (PrismContainerValue cVal: credentialsContainer.getValues()) {
+ processCredentialsCommonAdd(context, credentialsPath, cVal, now, task, result);
+ }
+ }
+ } else if (focusContext.isModify()) {
+ ObjectDelta focusDelta = focusContext.getDelta();
+ ContainerDelta containerDelta = focusDelta.findContainerDelta(credentialsPath);
+ if (containerDelta != null) {
+ if (containerDelta.isAdd()) {
+ for (PrismContainerValue cVal: containerDelta.getValuesToAdd()) {
+ processCredentialsCommonAdd(context, credentialsPath, cVal, now, task, result);
+ }
+ }
+ if (containerDelta.isReplace()) {
+ for (PrismContainerValue cVal: containerDelta.getValuesToReplace()) {
+ processCredentialsCommonAdd(context, credentialsPath, cVal, now, task, result);
+ }
+ }
+ } else {
+ if (hasValueDelta(focusDelta, credentialsPath)) {
+ Collection extends ItemDelta, ?>> metaDeltas = LensUtil.createModifyMetadataDeltas(context, credentialsPath.subPath(AbstractCredentialType.F_METADATA),
+ focusContext.getObjectDefinition(), now, task);
+ for (ItemDelta, ?> metaDelta: metaDeltas) {
+ context.getFocusContext().swallowToSecondaryDelta(metaDelta);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean hasValueDelta(ObjectDelta focusDelta, ItemPath credentialsPath) {
+ for (PartiallyResolvedDelta partialDelta: focusDelta.findPartial(credentialsPath)) {
+ LOGGER.trace("Residual delta:\n{}", partialDelta.debugDump());
+ ItemPath residualPath = partialDelta.getResidualPath();
+ if (residualPath == null || residualPath.isEmpty()) {
+ continue;
+ }
+ LOGGER.trace("PATH: {}", residualPath);
+ QName name = ItemPath.getFirstName(residualPath);
+ LOGGER.trace("NAME: {}", name);
+ if (isValueElement(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private void processCredentialsCommonAdd(LensContext context, ItemPath credentialsPath,
+ PrismContainerValue cVal, XMLGregorianCalendar now, Task task, OperationResult result)
+ throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
+ if (hasValueChange(cVal) && !hasMetadata(cVal)) {
+ MetadataType metadataType = LensUtil.createCreateMetadata(context, now, task);
+ ContainerDelta metadataDelta = ContainerDelta.createModificationAdd(credentialsPath.subPath(AbstractCredentialType.F_METADATA),
+ UserType.class, prismContext, metadataType);
+ context.getFocusContext().swallowToSecondaryDelta(metadataDelta);
+ }
+ }
+
+ private boolean hasValueChange(PrismContainerValue cVal) {
+ for (Item,?> item: cVal.getItems()) {
+ QName itemName = item.getElementName();
+ if (isValueElement(itemName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isValueElement(QName itemName) {
+ return !itemName.equals(AbstractCredentialType.F_FAILED_LOGINS) &&
+ !itemName.equals(AbstractCredentialType.F_LAST_FAILED_LOGIN) &&
+ !itemName.equals(AbstractCredentialType.F_LAST_SUCCESSFUL_LOGIN) &&
+ !itemName.equals(AbstractCredentialType.F_METADATA) &&
+ !itemName.equals(AbstractCredentialType.F_PREVIOUS_SUCCESSFUL_LOGIN);
+ }
+
+ private boolean hasMetadata(PrismContainerValue cVal) {
+ for (Item,?> item: cVal.getItems()) {
+ QName itemName = item.getElementName();
+ if (itemName.equals(AbstractCredentialType.F_METADATA)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/FocusProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/FocusProcessor.java
index 9d033d3f1f9..e84444aaa39 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/FocusProcessor.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/FocusProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2014 Evolveum
+ * Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -144,7 +144,7 @@ public class FocusProcessor {
private PrismContext prismContext;
@Autowired(required = true)
- private PasswordPolicyProcessor passwordPolicyProcessor;
+ private CredentialsProcessor credentialsProcessor;
@Autowired(required = true)
private ModelObjectResolver modelObjectResolver;
@@ -275,9 +275,9 @@ private void processFocusFocus(LensContext context, Str
now, task, result);
context.recompute();
- // PASSWORD POLICY
+ // CREDENTIALS (including PASSWORD POLICY)
- passwordPolicyProcessor.processPasswordPolicy(focusContext, context, result);
+ credentialsProcessor.processFocusCredentials(context, now, task, result);
// Processing done, check for success
diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/Projector.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/Projector.java
index cd0e8d5b323..bab68a8a384 100644
--- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/Projector.java
+++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/Projector.java
@@ -229,7 +229,7 @@ public void project(LensContext context, String activi
//SynchronizerUtil.traceContext("values", context, false);
if (consistencyChecks) context.checkConsistence();
- credentialsProcessor.processCredentials(context, projectionContext, task, result);
+ credentialsProcessor.processProjectionCredentials(context, projectionContext, now, task, result);
//SynchronizerUtil.traceContext("credentials", context, false);
if (consistencyChecks) context.checkConsistence();
diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestProjector.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestProjector.java
index 157c21cb599..a6858c29812 100644
--- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestProjector.java
+++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestProjector.java
@@ -1166,7 +1166,7 @@ public void test500ReconcileGuybrushDummy() throws Exception {
// There is an inbound mapping for password that generates it if not present. it is triggered in this case.
ObjectDelta userSecondaryDelta = context.getFocusContext().getSecondaryDelta();
assertTrue(userSecondaryDelta.getChangeType() == ChangeType.MODIFY);
- assertEquals("Unexpected number of modifications in user secondary delta", 5, userSecondaryDelta.getModifications().size());
+ assertEquals("Unexpected number of modifications in user secondary delta", 7, userSecondaryDelta.getModifications().size());
ItemDelta modification = userSecondaryDelta.getModifications().iterator().next();
assertEquals("Unexpected modification", PasswordType.F_VALUE, modification.getElementName());
assertOriginWithSideEffectChanges(userSecondaryDelta, OriginType.INBOUND);
@@ -1246,6 +1246,9 @@ private void assertOriginWithSideEffectChanges(ObjectDelta delta, Orig
PrismAsserts.assertOrigin(modification, OriginType.USER_POLICY);
iterator.remove();
}
+ if (modification.getPath().containsName(ObjectType.F_METADATA)) {
+ iterator.remove();
+ }
}
PrismAsserts.assertOrigin(delta,expectedOrigi);
}
diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java
index 6db45fc1405..5a305509675 100644
--- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java
+++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java
@@ -406,6 +406,7 @@ protected File getSystemConfigurationFile() {
protected Task createTask(String operationName) {
Task task = taskManager.createTaskInstance(operationName);
task.setOwner(userAdministrator);
+ task.setChannel(SchemaConstants.CHANNEL_GUI_USER_URI);
return task;
}
diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java
index a483287809a..5034306a96f 100644
--- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java
+++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java
@@ -24,6 +24,7 @@
import java.util.Collection;
import javax.xml.bind.JAXBException;
+import javax.xml.datatype.XMLGregorianCalendar;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
@@ -33,17 +34,23 @@
import org.testng.annotations.Test;
import com.evolveum.icf.dummy.resource.DummyAccount;
+import com.evolveum.midpoint.prism.Containerable;
+import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
+import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import com.evolveum.midpoint.task.api.Task;
+import com.evolveum.midpoint.test.IntegrationTestTools;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPolicyEnforcementType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;
+import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
@@ -68,17 +75,23 @@ public class TestPassword extends AbstractInitializedModelIntegrationTest {
private String accountOid;
private String accountRedOid;
+ private XMLGregorianCalendar lastPasswordChangeStart;
+ private XMLGregorianCalendar lastPasswordChangeEnd;
- public TestPassword() throws JAXBException {
- super();
- }
+ @Override
+ public void initSystem(Task initTask, OperationResult initResult) throws Exception {
+ super.initSystem(initTask, initResult);
+ login(USER_ADMINISTRATOR_USERNAME);
+ }
+
@Test
public void test010AddPasswordPolicy() throws Exception {
- TestUtil.displayTestTile(this, "test010AddPasswordPolicy");
+ final String TEST_NAME = "test010AddPasswordPolicy";
+ TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
- Task task = taskManager.createTaskInstance(TestPassword.class.getName() + ".test010AddPasswordPolicy");
+ Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.NONE);
@@ -103,13 +116,14 @@ public void test010AddPasswordPolicy() throws Exception {
@Test
public void test050CheckJackPassword() throws Exception {
- TestUtil.displayTestTile(this, "test050CheckJackPassword");
+ final String TEST_NAME = "test050CheckJackPassword";
+ TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN, WHEN
// this happens during test initialization when user-jack.xml is added
// THEN
- Task task = taskManager.createTaskInstance(TestPassword.class.getName() + ".test050CheckJackPassword");
+ Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
PrismObject userJack = getUser(USER_JACK_OID);
@@ -122,17 +136,21 @@ public void test050CheckJackPassword() throws Exception {
@Test
public void test051ModifyUserJackPassword() throws Exception {
- TestUtil.displayTestTile(this, "test051ModifyUserJackPassword");
+ final String TEST_NAME = "test051ModifyUserJackPassword";
+ TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
- Task task = taskManager.createTaskInstance(TestPassword.class.getName() + ".test051ModifyUserJackPassword");
+ Task task = createTask(TestPassword.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL);
ProtectedStringType userPasswordPs = new ProtectedStringType();
userPasswordPs.setClearValue(USER_PASSWORD_1_CLEAR);
-
+
+ XMLGregorianCalendar startCal = clock.currentTimeXMLGregorianCalendar();
+
// WHEN
+ TestUtil.displayWhen(TEST_NAME);
modifyUserReplace(USER_JACK_OID,
PASSWORD_VALUE_PATH,
task,
@@ -140,16 +158,21 @@ public void test051ModifyUserJackPassword() throws Exception {
userPasswordPs);
// THEN
+ TestUtil.displayThen(TEST_NAME);
result.computeStatus();
TestUtil.assertSuccess("executeChanges result", result);
+ XMLGregorianCalendar endCal = clock.currentTimeXMLGregorianCalendar();
+
PrismObject userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertUserJack(userJack, "Jack Sparrow");
assertEncryptedPassword(userJack, USER_PASSWORD_1_CLEAR);
+ assertPasswordMetadata(userJack, false, startCal, endCal);
}
+
@Test
public void test100ModifyUserJackAssignAccount() throws Exception {
TestUtil.displayTestTile(this, "test100ModifyUserJackAssignAccount");
@@ -203,6 +226,8 @@ public void test110ModifyUserJackPassword() throws Exception {
ProtectedStringType userPasswordPs = new ProtectedStringType();
userPasswordPs.setClearValue(USER_PASSWORD_2_CLEAR);
+
+ lastPasswordChangeStart = clock.currentTimeXMLGregorianCalendar();
// WHEN
modifyUserReplace(USER_JACK_OID,
@@ -215,12 +240,15 @@ public void test110ModifyUserJackPassword() throws Exception {
result.computeStatus();
TestUtil.assertSuccess("executeChanges result", result);
+ lastPasswordChangeEnd = clock.currentTimeXMLGregorianCalendar();
+
PrismObject userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertUserJack(userJack, "Jack Sparrow");
assertEncryptedPassword(userJack, USER_PASSWORD_2_CLEAR);
assertDummyPassword("jack", USER_PASSWORD_2_CLEAR);
+ assertPasswordMetadata(userJack, false, lastPasswordChangeStart, lastPasswordChangeEnd);
}
/**
@@ -258,6 +286,8 @@ public void test111ModifyAccountJackPassword() throws Exception {
assertEncryptedPassword(userJack, USER_PASSWORD_2_CLEAR);
// Account has new password
assertDummyPassword("jack", USER_PASSWORD_3_CLEAR);
+
+ assertPasswordMetadata(userJack, false, lastPasswordChangeStart, lastPasswordChangeEnd);
}
/**
@@ -283,6 +313,8 @@ public void test112ModifyJackPasswordUserAndAccount() throws Exception {
PASSWORD_VALUE_PATH, userPasswordPs5);
Collection> deltas = MiscSchemaUtil.createCollection(accountDelta, userDelta);
+
+ lastPasswordChangeStart = clock.currentTimeXMLGregorianCalendar();
// WHEN
modelService.executeChanges(deltas, null, task, result);
@@ -291,6 +323,8 @@ public void test112ModifyJackPasswordUserAndAccount() throws Exception {
result.computeStatus();
TestUtil.assertSuccess("executeChanges result", result);
+ lastPasswordChangeEnd = clock.currentTimeXMLGregorianCalendar();
+
PrismObject userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertUserJack(userJack, "Jack Sparrow");
@@ -299,6 +333,8 @@ public void test112ModifyJackPasswordUserAndAccount() throws Exception {
assertEncryptedPassword(userJack, USER_PASSWORD_4_CLEAR);
// Account has new password
assertDummyPassword("jack", USER_PASSWORD_5_CLEAR);
+
+ assertPasswordMetadata(userJack, false, lastPasswordChangeStart, lastPasswordChangeEnd);
}
/**
@@ -338,6 +374,8 @@ public void test120ModifyUserJackAssignAccountDummyRed() throws Exception {
// User and default dummy account should have unchanged passwords
assertEncryptedPassword(userJack, USER_PASSWORD_4_CLEAR);
assertDummyPassword("jack", USER_PASSWORD_5_CLEAR);
+
+ assertPasswordMetadata(userJack, false, lastPasswordChangeStart, lastPasswordChangeEnd);
}
/**
@@ -363,6 +401,8 @@ public void test121ModifyJackPasswordUserAndAccountRed() throws Exception {
PASSWORD_VALUE_PATH, userPasswordPs2);
Collection> deltas = MiscSchemaUtil.createCollection(userDelta, accountDelta);
+
+ lastPasswordChangeStart = clock.currentTimeXMLGregorianCalendar();
// WHEN
modelService.executeChanges(deltas, null, task, result);
@@ -371,6 +411,8 @@ public void test121ModifyJackPasswordUserAndAccountRed() throws Exception {
result.computeStatus();
TestUtil.assertSuccess("executeChanges result", result);
+ lastPasswordChangeEnd = clock.currentTimeXMLGregorianCalendar();
+
PrismObject userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertUserJack(userJack, "Jack Sparrow");
@@ -382,6 +424,8 @@ public void test121ModifyJackPasswordUserAndAccountRed() throws Exception {
// ... and default account has also the same password as user now. There was no other change on default dummy instance
// so even the weak mapping took place.
assertDummyPassword("jack", USER_PASSWORD_1_CLEAR);
+
+ assertPasswordMetadata(userJack, false, lastPasswordChangeStart, lastPasswordChangeEnd);
}
private void assertEncryptedPassword(PrismObject user, String expectedClearPassword) throws EncryptionException {
@@ -401,4 +445,24 @@ private void assertDummyPassword(String instance, String userId, String expected
assertEquals("Wrong password in dummy '"+instance+"' account "+userId, expectedClearPassword, account.getPassword());
}
+ private void assertPasswordMetadata(PrismObject user, boolean create, XMLGregorianCalendar start, XMLGregorianCalendar end) {
+ PrismContainer metadataContainer = user.findContainer(new ItemPath(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_METADATA));
+ assertNotNull("No password metadata in "+user, metadataContainer);
+ MetadataType metadataType = metadataContainer.getValue().asContainerable();
+ if (create) {
+ ObjectReferenceType creatorRef = metadataType.getCreatorRef();
+ assertNotNull("No creatorRef in password metadata in "+user, creatorRef);
+ assertEquals("Wrong creatorRef OID in password metadata in "+user, USER_ADMINISTRATOR_OID, creatorRef.getOid());
+ IntegrationTestTools.assertBetween("Wrong password create timestamp in password metadata in "+user, start, end, metadataType.getCreateTimestamp());
+ assertEquals("Wrong create channel", SchemaConstants.CHANNEL_GUI_USER_URI, metadataType.getCreateChannel());
+ } else {
+ ObjectReferenceType modifierRef = metadataType.getModifierRef();
+ assertNotNull("No modifierRef in password metadata in "+user, modifierRef);
+ assertEquals("Wrong modifierRef OID in password metadata in "+user, USER_ADMINISTRATOR_OID, modifierRef.getOid());
+ IntegrationTestTools.assertBetween("Wrong password modify timestamp in password metadata in "+user, start, end, metadataType.getModifyTimestamp());
+ assertEquals("Wrong modification channel", SchemaConstants.CHANNEL_GUI_USER_URI, metadataType.getModifyChannel());
+ }
+ }
+
+
}