Skip to content

Commit

Permalink
Extend delineation processing to entitlements
Browse files Browse the repository at this point in the history
Now we take filters into account also while searching for entitlements.
  • Loading branch information
mederly committed May 25, 2022
1 parent b6bf839 commit dbb0645
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.ConfigurationException;
Expand All @@ -29,7 +30,8 @@
import static com.evolveum.midpoint.util.MiscUtil.argCheck;

/**
* Methods for determining object definition for given kind/intent/object class.
* Methods for determining object definition for given kind/intent/object class. They build upon basic methods
* for definition lookup in {@link ResourceSchema}.
*
* There are two basic methods:
*
Expand Down Expand Up @@ -70,12 +72,12 @@ public class ResourceObjectDefinitionResolver {
String intent;

// Ignoring "UNKNOWN" values
if (shadow.getKind() == null || shadow.getKind() == ShadowKindType.UNKNOWN) {
if (ShadowUtil.isNotKnown(shadow.getKind())) {
kind = null;
intent = null;
} else {
kind = shadow.getKind();
if (SchemaConstants.INTENT_UNKNOWN.equals(shadow.getIntent())) {
if (ShadowUtil.isNotKnown(shadow.getIntent())) {
intent = null;
} else {
intent = shadow.getIntent();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2010-2022 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.provisioning.impl.resourceobjects;

import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.provisioning.impl.ProvisioningContext;
import com.evolveum.midpoint.schema.processor.*;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
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;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;

import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;

import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

import static com.evolveum.midpoint.provisioning.util.QueryConversionUtil.parseFilters;

/**
* Converts object type delineation into {@link SearchHierarchyConstraints} and additional filters to client-supplied query.
*/
@Component
class DelineationProcessor {

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

@Autowired private ResourceObjectReferenceResolver resourceObjectReferenceResolver;

QueryWithConstraints determineQueryWithConstraints(ProvisioningContext ctx, ObjectQuery clientQuery, OperationResult result)
throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException,
ConfigurationException, ObjectNotFoundException {
return new QueryWithConstraints(
createEffectiveQuery(ctx, clientQuery),
determineSearchHierarchyConstraints(ctx, result));
}

private SearchHierarchyConstraints determineSearchHierarchyConstraints(ProvisioningContext ctx, OperationResult result)
throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException,
ExpressionEvaluationException, SecurityViolationException {
if (!ctx.isTypeBased()) {
return null;
}
ResourceObjectTypeDefinition objectTypeDef = ctx.getObjectTypeDefinitionRequired();
ResourceObjectReferenceType baseContextRef = objectTypeDef.getBaseContext();
SearchHierarchyScope scope = objectTypeDef.getSearchHierarchyScope();

ResourceObjectIdentification baseContextIdentification = determineBaseContextIdentification(baseContextRef, ctx, result);

if (baseContextIdentification != null || scope != null) {
return new SearchHierarchyConstraints(baseContextIdentification, scope);
} else {
return null;
}
}

// ctx is type-based
@Nullable
private ResourceObjectIdentification determineBaseContextIdentification(
ResourceObjectReferenceType baseContextRef, ProvisioningContext ctx, OperationResult result)
throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException,
SecurityViolationException, ExpressionEvaluationException {

if (baseContextRef == null) {
return null;
}

ResourceObjectTypeDefinition objectTypeDef = ctx.getObjectTypeDefinitionRequired();
PrismObject<ShadowType> baseContextShadow;
try {
// We request the use of raw object class definition to avoid endless loops during base context determination.
baseContextShadow = resourceObjectReferenceResolver.resolve(
ctx, baseContextRef, true, "base context specification in " + objectTypeDef, result);
} catch (RuntimeException e) {
throw new SystemException("Cannot resolve base context for "+ objectTypeDef +", specified as "+ baseContextRef, e);
}
if (baseContextShadow == null) {
throw new ObjectNotFoundException("Base context not found for " + objectTypeDef + ", specified as " + baseContextRef);
}
ResourceObjectDefinition baseContextObjectDefinition =
java.util.Objects.requireNonNull(
ResourceObjectDefinitionResolver.getDefinitionForShadow(ctx.getResourceSchema(), baseContextShadow),
() -> "Couldn't determine definition for " + baseContextRef);
return ShadowUtil.getResourceObjectIdentification(baseContextShadow, baseContextObjectDefinition);
}

/**
* Combines client-specified query and the definition of the object type into a single query.
*/
private ObjectQuery createEffectiveQuery(ProvisioningContext ctx, ObjectQuery clientQuery) throws SchemaException {
ResourceObjectDefinition definition = ctx.getObjectDefinitionRequired();
LOGGER.trace("Computing effective query for {}", definition);
if (!(definition instanceof ResourceObjectTypeDefinition)) {
LOGGER.trace(" -> not a type definition, no change");
return clientQuery;
}
ResourceObjectTypeDefinition typeDefinition = (ResourceObjectTypeDefinition) definition;
List<SearchFilterType> filterClauses = typeDefinition.getDelineation().getAllFilterClauses();
LOGGER.trace(" -> found {} filter clause(s)", filterClauses.size());
ObjectQuery effectiveQuery = ObjectQueryUtil.addConjunctions(
clientQuery,
parseFilters(filterClauses, ctx.getObjectDefinitionRequired()));
LOGGER.trace("Effective query:\n{}", DebugUtil.debugDumpLazily(effectiveQuery, 1));
return effectiveQuery;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import javax.xml.namespace.QName;

import com.evolveum.midpoint.schema.processor.ResourceObjectTypeDefinition;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
Expand All @@ -27,7 +26,6 @@

import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -63,6 +61,7 @@ class EntitlementConverter {
private static final Trace LOGGER = TraceManager.getTrace(EntitlementConverter.class);

@Autowired private ResourceObjectReferenceResolver resourceObjectReferenceResolver;
@Autowired private DelineationProcessor delineationProcessor;
@Autowired private PrismContext prismContext;
@Autowired private MatchingRuleRegistry matchingRuleRegistry;

Expand Down Expand Up @@ -255,7 +254,7 @@ private <S extends ShadowType, T> void postProcessReadEntitlementToSubject(
private <S extends ShadowType> void executeSearchForEntitlements(
PrismContainer<ShadowAssociationType> associationContainer,
PrismObject<S> resourceObject,
ObjectQuery query,
ObjectQuery explicitQuery,
QName associationName,
ProvisioningContext subjectCtx,
ProvisioningContext entitlementCtx,
Expand All @@ -265,7 +264,8 @@ private <S extends ShadowType> void executeSearchForEntitlements(

ResourceObjectDefinition entitlementDef = entitlementCtx.getObjectDefinitionRequired();

SearchHierarchyConstraints searchHierarchyConstraints = determineSearchHierarchyConstraints(entitlementCtx, result);
QueryWithConstraints queryWithConstraints =
delineationProcessor.determineQueryWithConstraints(entitlementCtx, explicitQuery, result);

UcfObjectHandler handler = (ucfObject, lResult) -> {
PrismObject<ShadowType> entitlementResourceObject = ucfObject.getResourceObject();
Expand All @@ -286,15 +286,15 @@ private <S extends ShadowType> void executeSearchForEntitlements(
ConnectorInstance connector = subjectCtx.getConnector(ReadCapabilityType.class, result);
try {
LOGGER.trace("Processing entitlement-to-subject association for account {}: query {}",
ShadowUtil.getHumanReadableNameLazily(resourceObject), query);
ShadowUtil.getHumanReadableNameLazily(resourceObject), queryWithConstraints.query);
try {
connector.search(
entitlementDef,
query,
queryWithConstraints.query,
handler,
entitlementCtx.createAttributesToReturn(),
null,
searchHierarchyConstraints,
queryWithConstraints.constraints,
UcfFetchErrorReportingMethod.EXCEPTION,
subjectCtx.getUcfExecutionContext(),
result);
Expand Down Expand Up @@ -509,12 +509,11 @@ PrismObject<ShadowType> collectEntitlementsAsObjectOperationInShadowAdd(
* Collects entitlement changes from the shadow to entitlement section into attribute operations.
* NOTE: only collects SUBJECT_TO_ENTITLEMENT entitlement direction.
*/
public void collectEntitlementChange(
void collectEntitlementChange(
Collection<Operation> operations,
ContainerDelta<ShadowAssociationType> itemDelta,
ProvisioningContext ctx)
throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException,
ExpressionEvaluationException {
throws SchemaException {
OperationMap operationsMap = new OperationMap();

if (CollectionUtils.isNotEmpty(itemDelta.getValuesToReplace())) {
Expand Down Expand Up @@ -638,7 +637,8 @@ <T> void collectEntitlementsAsObjectOperationDelete(

ObjectQuery query = createEntitlementQuery(valueAttrValue, valueAttrDef, assocAttrDef, associationDef);

SearchHierarchyConstraints searchHierarchyConstraints = determineSearchHierarchyConstraints(entitlementCtx, result);
QueryWithConstraints queryWithConstraints =
delineationProcessor.determineQueryWithConstraints(entitlementCtx, query, result);

UcfObjectHandler handler = (ucfObject, lResult) -> {
PrismObject<ShadowType> entitlementShadow = ucfObject.getResourceObject();
Expand Down Expand Up @@ -676,15 +676,15 @@ <T> void collectEntitlementsAsObjectOperationDelete(
return true;
};
try {
LOGGER.trace("Searching for associations in deleted shadow, query: {}", query);
LOGGER.trace("Searching for associations in deleted shadow, query: {}", queryWithConstraints.query);
ConnectorInstance connector = subjectCtx.getConnector(ReadCapabilityType.class, result);
connector.search(
entitlementDef,
query,
queryWithConstraints.query,
handler,
entitlementCtx.createAttributesToReturn(),
null,
searchHierarchyConstraints,
queryWithConstraints.constraints,
UcfFetchErrorReportingMethod.EXCEPTION,
subjectCtx.getUcfExecutionContext(),
result);
Expand Down Expand Up @@ -1000,55 +1000,6 @@ private <TV,TA> PrismObject<ShadowType> collectEntitlementAsObjectOperation(
return attributesContainer;
}

// This is perhaps not the best place for this method. It has nothing to do with entitlements.
// But given class dependencies this is a very convenient place. Let's leave it here for now.
SearchHierarchyConstraints determineSearchHierarchyConstraints(ProvisioningContext ctx, OperationResult result)
throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException,
ExpressionEvaluationException, SecurityViolationException {
if (!ctx.isTypeBased()) {
return null;
}
ResourceObjectTypeDefinition objectTypeDef = ctx.getObjectTypeDefinitionRequired();
ResourceObjectReferenceType baseContextRef = objectTypeDef.getBaseContext();
SearchHierarchyScope scope = objectTypeDef.getSearchHierarchyScope();

ResourceObjectIdentification baseContextIdentification = determineBaseContextIdentification(baseContextRef, ctx, result);

if (baseContextIdentification != null || scope != null) {
return new SearchHierarchyConstraints(baseContextIdentification, scope);
} else {
return null;
}
}

// ctx is type-based
@Nullable private ResourceObjectIdentification determineBaseContextIdentification(
ResourceObjectReferenceType baseContextRef, ProvisioningContext ctx, OperationResult result)
throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException,
SecurityViolationException, ExpressionEvaluationException {

if (baseContextRef == null) {
return null;
}

ResourceObjectTypeDefinition objectTypeDef = ctx.getObjectTypeDefinitionRequired();
PrismObject<ShadowType> baseContextShadow;
try {
// We request the use of raw object class definition to avoid endless loops during base context determination.
baseContextShadow = resourceObjectReferenceResolver.resolve(
ctx, baseContextRef, true, "base context specification in " + objectTypeDef, result);
} catch (RuntimeException e) {
throw new SystemException("Cannot resolve base context for "+ objectTypeDef +", specified as "+ baseContextRef, e);
}
if (baseContextShadow == null) {
throw new ObjectNotFoundException("Base context not found for " + objectTypeDef + ", specified as " + baseContextRef);
}
ResourceObjectDefinition baseContextObjectDefinition =
java.util.Objects.requireNonNull(
ResourceObjectDefinitionResolver.getDefinitionForShadow(ctx.getResourceSchema(), baseContextShadow),
() -> "Couldn't determine definition for " + baseContextRef);
return ShadowUtil.getResourceObjectIdentification(baseContextShadow, baseContextObjectDefinition);
}
//endregion

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2010-2022 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.provisioning.impl.resourceobjects;

import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.processor.SearchHierarchyConstraints;

/**
* Aggregation of a search query with {@link SearchHierarchyConstraints}.
*
* Used as a return value of {@link DelineationProcessor} that waves object type delineation into client-specified
* search parameters.
*/
class QueryWithConstraints {

final ObjectQuery query;
final SearchHierarchyConstraints constraints;

QueryWithConstraints(ObjectQuery query, SearchHierarchyConstraints constraints) {
this.query = query;
this.constraints = constraints;
}
}

0 comments on commit dbb0645

Please sign in to comment.