Skip to content

Commit

Permalink
Prepare for fixing shadow lifecycle (MID-4833)
Browse files Browse the repository at this point in the history
We decided that the shadow.lifecycleState property should not be used
for so-called proposed shadows. This commit carries some preparatory
work. No functional changes, besides slight re-ordering of actions
in ShadowAddHelper.
  • Loading branch information
mederly committed Nov 10, 2022
1 parent 599a887 commit 1aee57e
Show file tree
Hide file tree
Showing 21 changed files with 180 additions and 251 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -799,8 +799,8 @@ public static ResourceAttribute<?> fixAttributePath(ResourceAttribute<?> attribu
}

// TODO: may be useful to move to ResourceObjectClassDefinition later?
public static void validateAttributeSchema(
ShadowType shadow, ResourceObjectDefinition objectDefinition) throws SchemaException {
public static void validateAttributeSchema(ShadowType shadow, ResourceObjectDefinition objectDefinition)
throws SchemaException {
ResourceAttributeContainer attributesContainer = getAttributesContainer(shadow);
for (ResourceAttribute<?> attribute: attributesContainer.getAttributes()) {
validateAttribute(attribute, objectDefinition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,8 +822,7 @@ public void test166PasswordLoginLifecycleProposedGoodPassword() throws Exception
Task task = getTestTask();
OperationResult result = task.getResult();

modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result,
SchemaConstants.LIFECYCLE_PROPOSED);
modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result, SchemaConstants.LIFECYCLE_PROPOSED);

loginJackGoodPasswordExpectDenied();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ default PrismObject<O> getObjectAny() {
/**
* @return List of all executed deltas (in fact, {@link ObjectDeltaOperation} objects).
*/
List<? extends ObjectDeltaOperation<?>> getExecutedDeltas();
List<? extends ObjectDeltaOperation<O>> getExecutedDeltas();

/**
* TODO is this method ever used?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ List<ObjectReferenceType> getMembersAsReferences(String orgOid)
/**
* Used for account activation notifier to collect all shadows which are going to be activated.
*/
List<ShadowType> getShadowsToActivate(Collection<? extends ModelElementContext> projectionContexts);
List<ShadowType> getShadowsToActivate(Collection<? extends ModelProjectionContext> projectionContexts);

String createRegistrationConfirmationLink(UserType userType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
*/
package com.evolveum.midpoint.model.impl.expr;

import static com.evolveum.midpoint.prism.delta.ObjectDelta.isAdd;
import static com.evolveum.midpoint.schema.GetOperationOptions.createNoFetchCollection;

import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull;

import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;

Expand Down Expand Up @@ -1434,24 +1433,17 @@ public CaseService getWorkflowService() {
}

@Override
public List<ShadowType> getShadowsToActivate(Collection<? extends ModelElementContext> projectionContexts) {
public List<ShadowType> getShadowsToActivate(Collection<? extends ModelProjectionContext> projectionContexts) {
List<ShadowType> shadows = new ArrayList<>();

//noinspection unchecked
for (ModelElementContext<ShadowType> projectionCtx : projectionContexts) {

List<? extends ObjectDeltaOperation> executedShadowDeltas = projectionCtx.getExecutedDeltas();
//noinspection unchecked
for (ObjectDeltaOperation<ShadowType> shadowDelta : executedShadowDeltas) {
if (shadowDelta.getExecutionResult().getStatus() == OperationResultStatus.SUCCESS
&& shadowDelta.getObjectDelta().getChangeType() == ChangeType.ADD) {
PrismObject<ShadowType> shadow = shadowDelta.getObjectDelta().getObjectToAdd();
PrismProperty<String> pLifecycleState = shadow.findProperty(ShadowType.F_LIFECYCLE_STATE);
if (pLifecycleState != null && !pLifecycleState.isEmpty() && SchemaConstants.LIFECYCLE_PROPOSED
.equals(pLifecycleState.getRealValue())) {
shadows.add(shadow.asObjectable());
for (ModelProjectionContext projectionCtx : projectionContexts) {
List<? extends ObjectDeltaOperation<ShadowType>> executedOperations = projectionCtx.getExecutedDeltas();
for (ObjectDeltaOperation<ShadowType> executedOperation : executedOperations) {
ObjectDelta<ShadowType> shadowDelta = executedOperation.getObjectDelta();
if (isAdd(shadowDelta) && executedOperation.getStatus() == OperationResultStatus.SUCCESS) {
ShadowType shadow = shadowDelta.getObjectToAdd().asObjectable();
if (SchemaConstants.LIFECYCLE_PROPOSED.equals(shadow.getLifecycleState())) {
shadows.add(shadow);
}

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,9 +558,8 @@ public boolean operationMatches(ChangeTypeType operation) {
//endregion

//region Executed deltas
@NotNull
@Override
public List<LensObjectDeltaOperation<O>> getExecutedDeltas() {
public @NotNull List<LensObjectDeltaOperation<O>> getExecutedDeltas() {
return executedDeltas;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
package com.evolveum.midpoint.notifications.impl.notifiers;

import java.util.Collection;
import java.util.Date;
import java.util.List;

Expand All @@ -16,7 +15,6 @@

import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.model.api.context.ModelElementContext;
import com.evolveum.midpoint.notifications.api.events.ModelEvent;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
Expand Down Expand Up @@ -149,8 +147,9 @@ private String getRequestorDisplayName(ObjectType requester) {
}

private List<ShadowType> getShadowsToActivate(ModelEvent modelEvent) {
Collection<? extends ModelElementContext<?>> projectionContexts = modelEvent.getProjectionContexts();
return getMidpointFunctions().getShadowsToActivate(projectionContexts);
return getMidpointFunctions()
.getShadowsToActivate(
modelEvent.getProjectionContexts());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,9 @@ public boolean isTypeBased() {
getObjectDefinitionRequired());
}

public void validateSchema(ShadowType shadow)
throws ObjectNotFoundException,
SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
public void validateSchemaIfConfigured(ShadowType shadow) throws SchemaException {
if (ResourceTypeUtil.isValidateSchema(resource)) {
ShadowUtil.validateAttributeSchema(shadow, getObjectDefinition());
ShadowUtil.validateAttributeSchema(shadow, resourceObjectDefinition);
}
}

Expand Down Expand Up @@ -594,14 +592,17 @@ public AttributesToReturn createAttributesToReturn() {

public ProvisioningContext applyAttributesDefinition(@NotNull PrismObject<ShadowType> shadow)
throws SchemaException, ConfigurationException {
return getCaretaker().applyAttributesDefinition(this, shadow);
return getCaretaker().applyAttributesDefinitionInNewContext(this, shadow);
}

public ProvisioningContext applyAttributesDefinition(@NotNull ShadowType shadow)
throws SchemaException, ConfigurationException {
return getCaretaker().applyAttributesDefinition(this, shadow);
return getCaretaker().applyAttributesDefinitionInNewContext(this, shadow);
}

/**
* Beware! For shadows being added, this method creates a separate (child) provisioning context.
*/
public void applyAttributesDefinition(@NotNull ObjectDelta<ShadowType> delta)
throws SchemaException, ConfigurationException {
getCaretaker().applyAttributesDefinition(this, delta);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ public ProvisioningContext createForShadow(
* Spawns the context for given shadow.
*
* Currently assumes that the resource OID is the same.
*
* TODO what if the shadow is "less-classified" (no kind/intent) than the original context?
*/
ProvisioningContext spawnForShadow(
@NotNull ProvisioningContext originalCtx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ public void determineShadowState(PrismObject<ShadowType> shadow, Task task, Oper
result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, ProvisioningServiceImpl.class);
try {
ProvisioningContext ctx = ctxFactory.createForShadow(shadow, task, result);
shadowsFacade.determineShadowState(ctx, shadow);
ctx.updateShadowState(shadow.asObjectable());
} catch (Throwable e) {
ProvisioningUtil.recordFatalErrorWhileRethrowing(LOGGER, result, null, e);
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ public class ShadowCaretaker {
@Autowired private Clock clock;

// Please use this method only via ProvisioningContext
public void applyAttributesDefinition(ProvisioningContext ctx, ObjectDelta<ShadowType> delta)
void applyAttributesDefinition(ProvisioningContext ctx, ObjectDelta<ShadowType> delta)
throws SchemaException, ConfigurationException {
if (delta.isAdd()) {
applyAttributesDefinition(ctx, delta.getObjectToAdd());
applyAttributesDefinitionInNewContext(ctx, delta.getObjectToAdd());
} else if (delta.isModify()) {
for (ItemDelta<?, ?> itemDelta : delta.getModifications()) {
if (SchemaConstants.PATH_ATTRIBUTES.equivalent(itemDelta.getParentPath())) {
Expand Down Expand Up @@ -137,36 +137,42 @@ private <V extends PrismValue, D extends ItemDefinition<?>> void applyAttributeD
}
}

// Please use this method only via ProvisioningContext
public ProvisioningContext applyAttributesDefinition(
@NotNull ProvisioningContext ctx, @NotNull ShadowType shadow) throws SchemaException, ConfigurationException {
return applyAttributesDefinition(ctx, shadow.asPrismObject());
/** See {@link #applyAttributesDefinitionInNewContext(ProvisioningContext, ShadowType)}. */
ProvisioningContext applyAttributesDefinitionInNewContext(
@NotNull ProvisioningContext ctx, @NotNull PrismObject<ShadowType> shadow) throws SchemaException, ConfigurationException {
return applyAttributesDefinitionInNewContext(ctx, shadow.asObjectable());
}

// Please use this method only via ProvisioningContext
public ProvisioningContext applyAttributesDefinition(
@NotNull ProvisioningContext ctx, @NotNull PrismObject<ShadowType> shadow)
/**
* Creates a new sub-context based on the kind/intent/class of the provided shadow, and applies the attribute definitions.
*
* TODO better name?
*
* @return the new sub-context
*/
ProvisioningContext applyAttributesDefinitionInNewContext(
@NotNull ProvisioningContext ctx, @NotNull ShadowType shadow)
throws SchemaException, ConfigurationException {
ProvisioningContext subctx = ctx.spawnForShadow(shadow.asObjectable());
subctx.assertDefinition();
ResourceObjectDefinition objectDefinition = subctx.getObjectDefinitionRequired();

PrismContainer<ShadowAttributesType> attributesContainer = shadow
.findContainer(ShadowType.F_ATTRIBUTES);
PrismObject<ShadowType> shadowObject = shadow.asPrismObject();
ProvisioningContext subContext = ctx.spawnForShadow(shadow);
subContext.assertDefinition();
ResourceObjectDefinition objectDefinition = subContext.getObjectDefinitionRequired();

PrismContainer<ShadowAttributesType> attributesContainer = shadowObject.findContainer(ShadowType.F_ATTRIBUTES);
if (attributesContainer != null) {
if (attributesContainer instanceof ResourceAttributeContainer) {
if (attributesContainer.getDefinition() == null) {
attributesContainer
.applyDefinition(objectDefinition.toResourceAttributeContainerDefinition());
attributesContainer.applyDefinition(objectDefinition.toResourceAttributeContainerDefinition());
}
} else {
try {
// We need to convert <attributes> to ResourceAttributeContainer
ResourceAttributeContainer convertedContainer = ResourceAttributeContainer
.convertFromContainer(attributesContainer, objectDefinition);
shadow.getValue().replace(attributesContainer, convertedContainer);
ResourceAttributeContainer convertedContainer =
ResourceAttributeContainer.convertFromContainer(attributesContainer, objectDefinition);
shadowObject.getValue().replace(attributesContainer, convertedContainer);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " in " + shadow, e);
throw e.wrap("Couldn't apply attributes definitions in " + shadow);
}
}
}
Expand All @@ -177,18 +183,18 @@ public ProvisioningContext applyAttributesDefinition(
// correctly because it will not be able to
// create the attribute container if needed.

PrismObjectDefinition<ShadowType> prismShadowDefinition = shadow.getDefinition();
PrismObjectDefinition<ShadowType> prismShadowDefinition = shadowObject.getDefinition();
PrismContainerDefinition<ShadowAttributesType> origAttrContainerDef = prismShadowDefinition
.findContainerDefinition(ShadowType.F_ATTRIBUTES);
if (!(origAttrContainerDef instanceof ResourceAttributeContainerDefinition)) {
PrismObjectDefinition<ShadowType> clonedDefinition =
prismShadowDefinition.cloneWithReplacedDefinition(
ShadowType.F_ATTRIBUTES, objectDefinition.toResourceAttributeContainerDefinition());
shadow.setDefinition(clonedDefinition);
shadowObject.setDefinition(clonedDefinition);
clonedDefinition.freeze();
}

return subctx;
return subContext;
}

/**
Expand Down Expand Up @@ -278,7 +284,7 @@ public ProvisioningContext reapplyDefinitions(
for (PendingOperationType pendingOperation2: repoShadow.getPendingOperation()) {
newPendingOperations.add(pendingOperation2.clone());
}
applyAttributesDefinition(ctx, resultShadow);
applyAttributesDefinitionInNewContext(ctx, resultShadow);
}
}
if (pendingDelta.isModify()) {
Expand Down Expand Up @@ -342,20 +348,11 @@ public ChangeTypeType findPreviousPendingLifecycleOperationInGracePeriod(
return found;
}


// NOTE: detection of quantum states (gestation, corpse) might not be precise. E.g. the shadow may already be
// tombstone because it is not in the snapshot. But as long as the pending operation is in grace we will still
// detect it as corpse. But that should not cause any big problems.
public @NotNull ShadowLifecycleStateType determineShadowState(
@NotNull ProvisioningContext ctx,
@NotNull ShadowType shadow,
@NotNull XMLGregorianCalendar now) {
return determineShadowStateInternal(ctx, shadow, now);
}

public @NotNull ShadowLifecycleStateType determineShadowState(
@NotNull ProvisioningContext ctx,
@NotNull ShadowType shadow) {
@NotNull ProvisioningContext ctx, @NotNull ShadowType shadow) {
return determineShadowStateInternal(ctx, shadow, clock.currentTimeXMLGregorianCalendar());
}

Expand All @@ -381,8 +378,8 @@ public ChangeTypeType findPreviousPendingLifecycleOperationInGracePeriod(
return ShadowLifecycleStateType.LIVE;
}
}
// FIXME currently we don't work correctly with the object lifecycle for shadows. E.g. some shadows are marked
// as proposed, even if they are active in fact. So be careful with this.
// FIXME This is wrong. Lifecycle is used for two independent purposes, see MID-4833.
// TODO we should use pending ADD instead
if (SchemaConstants.LIFECYCLE_PROPOSED.equals(shadow.getLifecycleState())) {
return ShadowLifecycleStateType.PROPOSED;
} else {
Expand All @@ -391,14 +388,9 @@ public ChangeTypeType findPreviousPendingLifecycleOperationInGracePeriod(
}

/** Determines and updates the shadow state. */
public void updateShadowState(ProvisioningContext ctx, PrismObject<ShadowType> shadow) {
updateShadowState(ctx, shadow.asObjectable());
}

/** Determines and updates the shadow state. */
public void updateShadowState(ProvisioningContext ctx, ShadowType shadow) {
ShadowLifecycleStateType state = determineShadowState(ctx, shadow);
shadow.setShadowLifecycleState(state);
void updateShadowState(ProvisioningContext ctx, ShadowType shadow) {
shadow.setShadowLifecycleState(
determineShadowState(ctx, shadow));
}

/** Determines and updates the shadow state - in situations where we don't have the context. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import org.springframework.stereotype.Component;

import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
Expand Down Expand Up @@ -47,9 +46,9 @@ void checkAdd(ProvisioningContext ctx, ShadowType shadow, OperationResult parent
ResourceAttributeContainer attributeCont = ShadowUtil.getAttributesContainer(shadow);

for (ResourceAttribute<?> attribute : attributeCont.getAttributes()) {
ResourceAttributeDefinition<?> attrDef = ctx.getObjectDefinitionRequired()
.findAttributeDefinitionRequired(attribute.getElementName());
PropertyLimitations limitations = attrDef.getLimitations(LayerType.MODEL);
PropertyLimitations limitations =
ctx.findAttributeDefinitionRequired(attribute.getElementName())
.getLimitations(LayerType.MODEL);
if (limitations == null) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ public ConstraintsCheckingResult check(OperationResult parentResult) throws Sche
}
}

@NotNull
private Collection<? extends ResourceAttributeDefinition<?>> getUniqueAttributesDefinitions() {
// What attributes should be used for uniqueness checking? Currently: all identifiers.
private @NotNull Collection<? extends ResourceAttributeDefinition<?>> getUniqueAttributesDefinitions() {
return provisioningContext.getObjectDefinitionRequired().getAllIdentifiers();
}

Expand Down

0 comments on commit 1aee57e

Please sign in to comment.