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> createModifyMetadataDeltas(LensContext context, + ItemPath metadataPath, PrismObjectDefinition def, XMLGregorianCalendar now, Task task) { + Collection> 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)context, projectionContext, task, result); -// return; + processFocusPassword((LensContext)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)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> 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()); + } + } + + }