Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Evolveum/midpoint
Browse files Browse the repository at this point in the history
* 'master' of github.com:Evolveum/midpoint:
  Fix MID-9080 entlitlements are not showing
  Make shadow lookup by secondary ID more robust
  • Loading branch information
katkav committed Sep 19, 2023
2 parents 0bd7fb8 + 58b31fc commit c5bd297
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package com.evolveum.midpoint.gui.impl.page.admin.resource.component;

import static com.evolveum.midpoint.common.LocalizationTestUtil.getLocalizationService;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
Expand Down Expand Up @@ -54,16 +56,14 @@
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.builder.S_FilterEntry;
import com.evolveum.midpoint.prism.query.builder.S_FilterExit;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.processor.ResourceObjectDefinition;
import com.evolveum.midpoint.schema.processor.ResourceObjectTypeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceSchema;
import com.evolveum.midpoint.schema.processor.ResourceSchemaFactory;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
import com.evolveum.midpoint.schema.util.task.ActivityDefinitionBuilder;
Expand Down Expand Up @@ -689,15 +689,11 @@ protected ObjectQuery getCustomizeContentQuery() {
protected Integer countObjects(Class<ShadowType> type, ObjectQuery query, Collection<SelectorOptions<GetOperationOptions>> currentOptions, Task task, OperationResult result) throws CommonException {
Integer count = 0;
ResourceType resource = getObjectDetailsModels().getObjectType();
ShadowKindType kind = getKind();
if (kind != null) {
ResourceSchema resourceSchema = ResourceSchemaFactory.getCompleteSchemaRequired(resource);
@Nullable ResourceObjectDefinition objectTypeDefinition = resourceSchema.findDefaultDefinitionForKind(kind);
if (objectTypeDefinition != null) {
count = super.countObjects(type, query, currentOptions, task, result);
} else {
warn(String.format("No object type definition for %s/%s in %s", kind, getIntent(), resource));
}
if (getSelectedObjectType() == null) {
StringResourceModel warnMessage = createStringResource("PageResource.warn.no.object.definition", getKind(), getIntent(), resource);
String localeWarnMessage = getLocalizationService()
.translate(PolyString.fromOrig(warnMessage.getString()), WebComponentUtil.getCurrentLocale(), true);
warn(localeWarnMessage);
} else {
count = super.countObjects(type, query, currentOptions, task, result);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,20 @@ class ResourceObjectReferenceResolver {

/**
* Resolve primary identifier from a collection of identifiers that may contain only secondary identifiers.
*
* We accept also dead shadows, but only if there is only one. (This is a bit inconsistent, should be fixed somehow.)
* Actually, we could be more courageous, and reject dead shadows altogether, as we use the result for object modification;
* but there is a theoretical chance that the shadow is dead in the repo but alive on the resource.
* See also {@link #resolvePrimaryIdentifiers(ProvisioningContext, ResourceObjectIdentification, OperationResult)}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
Collection<? extends ResourceAttribute<?>> resolvePrimaryIdentifier(ProvisioningContext ctx,
Collection<? extends ResourceAttribute<?>> identifiers, final String desc, OperationResult result)
throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException,
ExpressionEvaluationException {
Collection<? extends ResourceAttribute<?>> resolvePrimaryIdentifier(
ProvisioningContext ctx,
Collection<? extends ResourceAttribute<?>> identifiers,
final String desc,
OperationResult result)
throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException,
ExpressionEvaluationException {
if (identifiers == null) {
return null;
}
Expand Down Expand Up @@ -202,7 +210,8 @@ Collection<? extends ResourceAttribute<?>> resolvePrimaryIdentifier(Provisioning
/**
* @param repoShadow Used when read capability is "caching only"
*/
PrismObject<ShadowType> fetchResourceObject(ProvisioningContext ctx,
PrismObject<ShadowType> fetchResourceObject(
ProvisioningContext ctx,
Collection<? extends ResourceAttribute<?>> identifiers,
AttributesToReturn attributesToReturn,
@Nullable PrismObject<ShadowType> repoShadow,
Expand Down Expand Up @@ -262,10 +271,15 @@ PrismObject<ShadowType> fetchResourceObject(ProvisioningContext ctx,

/**
* Resolve primary identifier from a collection of identifiers that may contain only secondary identifiers.
*
* We accept also dead shadows, but only if there is only one. (This is a bit inconsistent, should be fixed somehow.)
* Actually, we could be more courageous, and reject dead shadows altogether, as we use the result for object fetching;
* but there is a theoretical chance that the shadow is dead in the repo but alive on the resource.
* See also {@link #resolvePrimaryIdentifier(ProvisioningContext, Collection, String, OperationResult)}.
*/
@SuppressWarnings("unchecked")
private ResourceObjectIdentification resolvePrimaryIdentifiers(ProvisioningContext ctx,
ResourceObjectIdentification identification, OperationResult result)
private ResourceObjectIdentification resolvePrimaryIdentifiers(
ProvisioningContext ctx, ResourceObjectIdentification identification, OperationResult result)
throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException,
ExpressionEvaluationException {
if (identification == null) {
Expand All @@ -274,7 +288,8 @@ private ResourceObjectIdentification resolvePrimaryIdentifiers(ProvisioningConte
if (identification.hasPrimaryIdentifiers()) {
return identification;
}
Collection<ResourceAttribute<?>> secondaryIdentifiers = (Collection<ResourceAttribute<?>>) identification.getSecondaryIdentifiers();
Collection<ResourceAttribute<?>> secondaryIdentifiers =
(Collection<ResourceAttribute<?>>) identification.getSecondaryIdentifiers();
PrismObject<ShadowType> repoShadow = shadowFinder.lookupShadowBySecondaryIds(ctx, secondaryIdentifiers, result);
if (repoShadow == null) {
// TODO: we should attempt resource search here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
Expand Down Expand Up @@ -85,7 +86,11 @@ public OperationResultStatus handleAddError(
LOGGER.trace("Going to find matching shadows using the query:\n{}", query.debugDumpLazily(1));
List<PrismObject<ShadowType>> matchingShadows = shadowFinder.searchShadows(ctx, query, null, result);
LOGGER.trace("Found {}: {}", matchingShadows.size(), matchingShadows);
ShadowType liveShadow = asObjectable(selectLiveShadow(matchingShadows));
ShadowType liveShadow =
asObjectable(
selectLiveShadow(
matchingShadows,
DebugUtil.lazy(() -> "when looking by secondary identifier: " + query)));
LOGGER.trace("Live shadow found: {}", liveShadow);

if (liveShadow != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private void discoverConflictingShadow(

ObjectQuery query = createQueryBySecondaryIdentifier(newShadow);
List<PrismObject<ShadowType>> conflictingRepoShadows = shadowFinder.searchShadows(ctx, query, null, result);
PrismObject<ShadowType> oldShadow = selectLiveShadow(conflictingRepoShadows);
PrismObject<ShadowType> oldShadow = selectLiveShadow(conflictingRepoShadows, "(conflicting repo shadows)");
if (oldShadow != null) {
ctx.applyAttributesDefinition(oldShadow);
}
Expand All @@ -115,7 +115,8 @@ private void discoverConflictingShadow(

final List<PrismObject<ShadowType>> conflictingResourceShadows =
findConflictingShadowsOnResource(query, ctx.getTask(), result);
PrismObject<ShadowType> conflictingShadow = selectLiveShadow(conflictingResourceShadows);
PrismObject<ShadowType> conflictingShadow =
selectLiveShadow(conflictingResourceShadows, "(conflicting shadows)");

LOGGER.trace("DISCOVERY: found conflicting shadow for {}:\n{}", newShadow,
conflictingShadow == null ? " no conflicting shadow" : conflictingShadow.debugDumpLazily(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ public ShadowType lookupLiveShadowByPrimaryId(
List<PrismObject<ShadowType>> shadowsFound = searchRepoShadows(query, zeroStalenessOptions(), result);
LOGGER.trace("Found {} shadows (live or dead)", shadowsFound.size());

PrismObject<ShadowType> liveShadow = selectLiveShadow(shadowsFound);
PrismObject<ShadowType> liveShadow =
selectLiveShadow(shadowsFound, "when looking by primary identifier " + primaryIdentifier);
checkConsistency(liveShadow);
return asObjectable(liveShadow);
}
Expand Down Expand Up @@ -178,7 +179,8 @@ public ShadowType lookupLiveShadowByAllIds(

List<PrismObject<ShadowType>> shadows = searchRepoShadows(query, null, result);

PrismObject<ShadowType> singleShadow = ProvisioningUtil.selectLiveShadow(shadows);
PrismObject<ShadowType> singleShadow =
ProvisioningUtil.selectLiveShadow(shadows, "when looking by IDs: " + identifierContainer);
checkConsistency(singleShadow);
return asObjectable(singleShadow);
}
Expand Down Expand Up @@ -214,7 +216,7 @@ public PrismObject<ShadowType> lookupShadowBySecondaryIds(ProvisioningContext ct
Collection<ResourceAttribute<?>> secondaryIdentifiers, OperationResult result)
throws SchemaException {
List<PrismObject<ShadowType>> shadows = searchShadowsBySecondaryIds(ctx, secondaryIdentifiers, result);
return ProvisioningUtil.selectSingleShadow(shadows, lazy(() -> "secondary identifiers: " + secondaryIdentifiers));
return ProvisioningUtil.selectSingleShadowRelaxed(shadows, lazy(() -> "secondary identifiers: " + secondaryIdentifiers));
}

private List<PrismObject<ShadowType>> searchShadowsBySecondaryIds(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ public static boolean isDoDiscovery(@NotNull ResourceType resource, Provisioning
public static PrismObject<ShadowType> selectSingleShadow(@NotNull List<PrismObject<ShadowType>> shadows, Object context) {
LOGGER.trace("Selecting from {} objects", shadows.size());

if (shadows.size() == 0) {
if (shadows.isEmpty()) {
return null;
} else if (shadows.size() > 1) {
LOGGER.error("Too many shadows ({}) for {}", shadows.size(), context);
Expand All @@ -581,24 +581,48 @@ public static PrismObject<ShadowType> selectSingleShadow(@NotNull List<PrismObje
}
}

/**
* As {@link #selectSingleShadow(List, Object)} but allows the existence of multiple dead shadows
* (if single live shadow exists). Not very nice! Transitional solution until better one is found.
*/
@Nullable
public static PrismObject<ShadowType> selectSingleShadowRelaxed(
@NotNull List<PrismObject<ShadowType>> shadows, Object context) {
var singleLive = selectLiveShadow(shadows, context);
if (singleLive != null) {
return singleLive;
}

// all remaining shadows (if any) are dead
if (shadows.isEmpty()) {
return null;
} else if (shadows.size() > 1) {
LOGGER.error("Cannot select from dead shadows ({}) for {}", shadows.size(), context);
LOGGER.debug("Shadows:\n{}", DebugUtil.debugDumpLazily(shadows));
throw new IllegalStateException("More than one [dead] shadow for " + context);
} else {
return shadows.get(0);
}
}

// TODO better place?
@Nullable
public static PrismObject<ShadowType> selectLiveShadow(List<PrismObject<ShadowType>> shadows) {
public static PrismObject<ShadowType> selectLiveShadow(List<PrismObject<ShadowType>> shadows, Object context) {
if (shadows == null || shadows.isEmpty()) {
return null;
}

List<PrismObject<ShadowType>> liveShadows = shadows.stream()
.filter(ShadowUtil::isNotDead)
.collect(Collectors.toList());
.toList();

if (liveShadows.isEmpty()) {
return null;
} else if (liveShadows.size() > 1) {
LOGGER.trace("More than one live shadow found ({} out of {}):\n{}",
liveShadows.size(), shadows.size(), DebugUtil.debugDumpLazily(shadows, 1));
LOGGER.trace("More than one live shadow found ({} out of {}) {}\n{}",
liveShadows.size(), shadows.size(), context, DebugUtil.debugDumpLazily(shadows, 1));
// TODO: handle "more than one shadow" case for conflicting shadows - MID-4490
throw new IllegalStateException("Found more than one live shadow: " + liveShadows);
throw new IllegalStateException("Found more than one live shadow " + context + ": " + liveShadows);
} else {
return liveShadows.get(0);
}
Expand All @@ -611,7 +635,7 @@ public static PrismObject<ShadowType> selectLiveShadow(List<PrismObject<ShadowTy
* TODO better place?
*/
public static ShadowType selectLiveOrAnyShadow(List<PrismObject<ShadowType>> shadows) {
PrismObject<ShadowType> liveShadow = ProvisioningUtil.selectLiveShadow(shadows);
PrismObject<ShadowType> liveShadow = ProvisioningUtil.selectLiveShadow(shadows, "");
if (liveShadow != null) {
return liveShadow.asObjectable();
} else if (shadows.isEmpty()) {
Expand Down

0 comments on commit c5bd297

Please sign in to comment.