Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4091,6 +4091,20 @@
<xsd:complexContent>
<xsd:extension base="tns:CollectionSpecificationType">
<xsd:sequence>
<xsd:element name="type" type="xsd:QName" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Type of objects that belong to this collection. This is used when the collection
is specified by an inline filter and the type cannot be derived from a referenced
collection or from the containing configuration.
</xsd:documentation>
<xsd:appinfo>
<a:since>4.11</a:since>
<a:displayName>CollectionSpecificationType.type</a:displayName>
<a:displayOrder>5</a:displayOrder>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:choice>
<xsd:element name="collectionRef" type="tns:ObjectReferenceType" minOccurs="0">
<xsd:annotation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,16 +298,19 @@ private void compileObjectCollectionView(
} else if (collectionSpec.getFilter() != null) {

SearchFilterType filter = collectionSpec.getFilter();
Class<?> effectiveTargetTypeClass = determineTargetTypeClass(collectionSpec, targetTypeClass);

CollectionRefSpecificationType baseCollectionSpec = collectionSpec.getBaseCollectionRef();
if (baseCollectionSpec == null) {
if (targetTypeClass == null) {
if (effectiveTargetTypeClass == null) {
throw new IllegalArgumentException("UndefinedTypeForCollection type: " + collectionSpec);
}
ObjectFilter objectFilter = prismContext.getQueryConverter().parseFilter(filter, targetTypeClass);
ObjectFilter objectFilter = prismContext.getQueryConverter().parseFilter(filter, effectiveTargetTypeClass);
existingView.setFilter(objectFilter);
setContainerTypeIfNeeded(existingView, collectionSpec.getType());
} else {
compileBaseCollectionSpec(filter, existingView, baseCollectionSpec, targetTypeClass, task, result);
compileBaseCollectionSpec(filter, existingView, baseCollectionSpec, effectiveTargetTypeClass, task, result);
setContainerTypeIfNeeded(existingView, collectionSpec.getType());
}
} else {
// E.g. the case of empty domain specification. Nothing to do. Just return what we have.
Expand All @@ -316,6 +319,45 @@ private void compileObjectCollectionView(
}
}

/**
* Determines the type to be used for parsing an inline collection filter.
*
* The type can come either from the surrounding configuration, e.g. object
* list view or base collection, or from the collection specification itself.
* The explicit collection type is mainly needed for standalone inline filters,
* where there is no referenced object collection that would provide the type.
*
* If both types are present, the explicit collection type may only keep or
* narrow the surrounding type. It must not broaden it or point to an unrelated
* type, because the filter is evaluated in the context of the effective type.
*/
private Class<?> determineTargetTypeClass(
CollectionRefSpecificationType collectionSpec,
Class<?> targetTypeClass) throws ConfigurationException {

QName explicitType = collectionSpec.getType();
if (explicitType == null) {
return targetTypeClass;
}

Class<?> explicitTargetTypeClass =
prismContext.getSchemaRegistry().determineClassForType(explicitType);

if (targetTypeClass != null && !targetTypeClass.isAssignableFrom(explicitTargetTypeClass)) {
throw new ConfigurationException(
"Conflicting collection types: " + targetTypeClass.getSimpleName()
+ " and " + explicitType);
}

return explicitTargetTypeClass;
}

private void setContainerTypeIfNeeded(CompiledObjectCollectionView existingView, QName collectionType) {
if (existingView.getContainerType() == null && collectionType != null) {
existingView.setContainerType(collectionType);
}
}

private void compileArchetypeCollectionView(
CompiledObjectCollectionView existingView,
Class<?> targetTypeClass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;

import java.util.Collection;
import java.util.stream.Collectors;

import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

Expand All @@ -24,10 +26,12 @@
import com.evolveum.midpoint.model.api.authentication.CompiledObjectCollectionView;
import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.ConfigurationException;

/**
* Test behavior of connectors that have several instances (poolable connectors).
Expand Down Expand Up @@ -116,6 +120,70 @@ public void test102SearchCollectionUsers() throws Exception {
assertEquals("Wrong number of users in collection", getNumberOfUsers(), users.size());
}

/**
* MID-9879: Inline collection filter can be evaluated when the collection spec provides its object type.
* The explicit type is also allowed to narrow a broader containing target type.
*/
@Test
public void test103CompileInlineTypedCollectionView() throws Exception {
// GIVEN
Task task = getTestTask();
OperationResult result = task.getResult();

ObjectFilter enabledUsersFilter = prismContext.queryFor(UserType.class)
.item(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS).eq(ActivationStatusType.ENABLED)
.buildFilter();
CollectionRefSpecificationType inlineTypedCollection = new CollectionRefSpecificationType()
.type(UserType.COMPLEX_TYPE)
.filter(prismContext.getQueryConverter().createSearchFilterType(enabledUsersFilter));

// WHEN
when();
CompiledObjectCollectionView inlineTypedCollectionView =
modelInteractionService.compileObjectCollectionView(inlineTypedCollection, null, task, result);
CompiledObjectCollectionView inlineTypedCollectionViewFromFocus =
modelInteractionService.compileObjectCollectionView(inlineTypedCollection, FocusType.class, task, result);
CollectionStats stats = modelInteractionService.determineCollectionStats(inlineTypedCollectionView, task, result);

// THEN
then();
displayDumpable("Inline typed collection view", inlineTypedCollectionView);
assertSuccess(result);

assertObjectCollectionView(inlineTypedCollectionView)
.assertFilter();
assertEquals("Wrong container type", UserType.COMPLEX_TYPE, inlineTypedCollectionView.getContainerType());
assertEquals("Wrong narrowed container type", UserType.COMPLEX_TYPE, inlineTypedCollectionViewFromFocus.getContainerType());
assertEquals("Wrong target class", UserType.class, inlineTypedCollectionView.getTargetClass());
assertEquals("Wrong narrowed target class", UserType.class, inlineTypedCollectionViewFromFocus.getTargetClass());
assertEquals("Wrong object count", (Integer) getNumberOfUsers(), stats.getObjectCount());
}

/**
* MID-9879: Inline collection type must not conflict with the containing target type.
*/
@Test
public void test104RejectConflictingInlineCollectionType() throws Exception {
// GIVEN
Task task = getTestTask();
OperationResult result = task.getResult();

ObjectFilter enabledUsersFilter = prismContext.queryFor(UserType.class)
.item(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS).eq(ActivationStatusType.ENABLED)
.buildFilter();
CollectionRefSpecificationType inlineTypedCollection = new CollectionRefSpecificationType()
.type(UserType.COMPLEX_TYPE)
.filter(prismContext.getQueryConverter().createSearchFilterType(enabledUsersFilter));

// WHEN
when();
assertConflictingInlineCollectionType(inlineTypedCollection, RoleType.class, task, result);

CollectionRefSpecificationType inlineBroaderTypedCollection = inlineTypedCollection.clone()
.type(FocusType.COMPLEX_TYPE);
assertConflictingInlineCollectionType(inlineBroaderTypedCollection, UserType.class, task, result);
}

@Test
public void test110CollectionStatsAllEnabled() throws Exception {
// GIVEN
Expand Down Expand Up @@ -201,4 +269,21 @@ public void test122EvaluateRulesOneDisabled() throws Exception {
.assertConstraintKind(PolicyConstraintKindType.COLLECTION_STATS);

}

private void assertConflictingInlineCollectionType(
CollectionRefSpecificationType inlineTypedCollection, Class<? extends Containerable> targetTypeClass,
Task task, OperationResult result)
throws Exception {

try {
modelInteractionService.compileObjectCollectionView(inlineTypedCollection, targetTypeClass, task, result);
} catch (ConfigurationException e) {
displayExpectedException(e);
assertTrue("Unexpected exception message: " + e.getMessage(),
e.getMessage().contains("Conflicting collection types"));
return;
}
throw new AssertionError("Expected conflicting inline collection type to be rejected for "
+ targetTypeClass.getSimpleName());
}
}
1 change: 1 addition & 0 deletions release-notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Overall, midPoint 4.10 opens up the world of identity management and governance
* Delineation suggestions: filter parsing broken after recent GUI change. See bug:MID-11175[]
* Fixed work item search by name causing repository mapping error. See bug:MID-8834[]
* Fixed translation of archetype display labels in assignment picker and summary panel. See bug:MID-11177[]
* Fixed dashboard widget collections with inline filters by allowing explicit object type specification. See bug:MID-9879[]

=== Releases Of Other Components

Expand Down
Loading