Skip to content

Commit

Permalink
Fix/adapt TestUnix story test
Browse files Browse the repository at this point in the history
The binding attribute at the subject side was not cached. It is now
declared as a secondary identifier.

The error message for these cases was improved.

Moreover, there is a slight change in displaying association values,
please see the addition to the release notes. To be discussed, though.
  • Loading branch information
mederly committed Mar 6, 2024
1 parent ee257a7 commit 67c157d
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
package com.evolveum.midpoint.schema.processor;

import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectReferenceType;

import org.jetbrains.annotations.NotNull;
Expand All @@ -24,7 +26,7 @@
*
* TODO later, we may move filter(s) here as well
*/
public abstract class ResourceObjectSetDelineation implements Serializable {
public abstract class ResourceObjectSetDelineation implements DebugDumpable, Serializable {

/**
* There is intentionally only a single class name here. The reason is that we cannot execute resource searches
Expand Down Expand Up @@ -62,4 +64,17 @@ public abstract class ResourceObjectSetDelineation implements Serializable {
public @NotNull Collection<ObjectFilter> getFilterClauses() {
return filterClauses;
}

@Override
public String debugDump(int indent) {
var sb = DebugUtil.createTitleStringBuilderLn(getClass(), indent);
DebugUtil.debugDumpWithLabelLn(sb, "objectClassName", objectClassName, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "baseContext", baseContext, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "searchHierarchyScope", searchHierarchyScope, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "filterClauses", filterClauses, indent + 1);
extendDebugDump(sb, indent);
return sb.toString();
}

abstract void extendDebugDump(StringBuilder sb, int indent);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.Objects;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.DebugUtil;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -115,4 +117,11 @@ public static ResourceObjectTypeDelineation of(
Integer getClassificationOrder() {
return delineationBean.getClassificationOrder();
}

@Override
void extendDebugDump(StringBuilder sb, int indent) {
DebugUtil.debugDumpWithLabelLn(sb, "auxiliaryObjectClassNames", auxiliaryObjectClassNames, indent + 1);
DebugUtil.debugDumpWithLabelLn(sb, "condition", String.valueOf(condition), indent + 1); // TODO
DebugUtil.debugDumpWithLabel(sb, "classificationOrder", getClassificationOrder(), indent + 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

package com.evolveum.midpoint.schema.processor;

import com.evolveum.midpoint.schema.config.AssociationConfigItem;
import com.evolveum.midpoint.schema.config.AssociationConfigItem.AttributeBinding;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectReferenceType;
Expand Down Expand Up @@ -69,4 +69,10 @@ private <T> ResourceAttributeDefinition<T> getObjectAttributeDefinition(QName at
throw SystemException.unexpected(e, "(already checked at schema parse time)");
}
}

@Override
void extendDebugDump(StringBuilder sb, int indent) {
DebugUtil.debugDumpWithLabelToStringLn(sb, "objectDefinition", objectDefinition, indent + 1);
DebugUtil.debugDumpWithLabel(sb, "auxiliaryObjectClassName", auxiliaryObjectClassName, indent + 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,21 @@ private ResourceObjectDiscriminator getEntitlementDiscriminator(
PrismPropertyValue<?> subjectAttrValue = searchOp.getSubjectAttrValue();
if (subjectAttrValue == null) {
// This is very unfortunate situation. We wanted to delete all mentions of the subject in entitlement objects
// (like groups), but the subject has no identifier! We can nothing to do, but let us at least report the problem.
LOGGER.error("No identifier of the subject:\n{}\nSimulation definition:\n{}\nSubject context:\n{}",
subjectRepoShadow.debugDump(1), simulationDefinition.debugDump(1),
subjectCtx.debugDump(1));
throw new SchemaException("No identifier of the subject to be deleted! Cannot delete entitlement associations.");
// (like groups), but the subject shadow has no identifier! Most probably the value is not cached.
LOGGER.error("""
No value of binding attribute '{}' in the subject repo shadow:
{}
Simulation definition:
{}
Subject context:
{}
Most probably, the attribute is not cached. The recommended way is to mark it as a secondary identifier.""",
searchOp.getSubjectAttrName(), subjectRepoShadow.debugDump(1),
simulationDefinition.debugDump(1), subjectCtx.debugDump(1));
throw new SchemaException(String.format(
"Cannot delete associations for the subject, as the value of binding attribute '%s' "
+ "is unknown or missing in the subject shadow.", searchOp.getSubjectAttrName()));
}

// .. and remove it from each of the entitlement objects (e.g. group) found for that account.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.xml.namespace.QName;

import static com.evolveum.midpoint.provisioning.impl.resourceobjects.DelineationProcessor.determineQueryWithConstraints;
import static com.evolveum.midpoint.provisioning.impl.resourceobjects.EntitlementUtils.createEntitlementQuery;
import static com.evolveum.midpoint.provisioning.impl.resourceobjects.EntitlementUtils.getSingleValue;
Expand All @@ -45,7 +47,13 @@ class EntitlementObjectSearch<T> {
@NotNull private final ShadowType subject;

/**
* The value according to which we are searching the entitlements. Typically the account DN.
* The name of the attribute (on the subject) according to the value of which we are searching the entitlements.
* Typically the account DN.
*/
@NotNull private final QName subjectAttrName;

/**
* The value of {@link #subjectAttrName} according to which we are searching the entitlements.
* Determined during construction. If null, the search cannot be done.
*/
@Nullable private final PrismPropertyValue<T> subjectAttrValue;
Expand All @@ -63,7 +71,7 @@ class EntitlementObjectSearch<T> {
this.attributeBinding = attributeBinding;
this.subject = subject;

var subjectAttrName = attributeBinding.subjectSide(); // e.g. ri:dn
this.subjectAttrName = attributeBinding.subjectSide(); // e.g. ri:dn
var subjectAssocName = simulationDefinition.getLocalSubjectItemName();

this.subjectAttrValue = getSingleValue(subject, subjectAttrName, subjectAssocName, errorCtx);
Expand Down Expand Up @@ -105,6 +113,10 @@ public void execute(@NotNull ResourceObjectHandler foundObjectsHandler, Operatio
}
}

public @NotNull QName getSubjectAttrName() {
return subjectAttrName;
}

@Nullable PrismPropertyValue<T> getSubjectAttrValue() {
return subjectAttrValue;
}
Expand Down
5 changes: 5 additions & 0 deletions release-notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,11 @@ See bug:MID-9220[].
* Automatic caching of association binding attributes (the "value" side, i.e. `valueAttribute` in the association definition) is no longer provided.
It is recommended to mark them as secondary identifiers.

* The filtering of associations was changed slightly.
In particular, even if the required auxiliary object class is not present for the subject, the association values are still shown - if they exist on the resource.
(They were hidden before.)
#TODO reconsider this#

=== Java and REST API Changes Since 4.8

NOTE: As for the Java API, this section describes changes in `midpoint` and `basic` function libraries.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package com.evolveum.midpoint.testing.story;

import static com.evolveum.midpoint.test.util.MidPointTestConstants.QNAME_DN;
import static com.evolveum.midpoint.test.util.MidPointTestConstants.QNAME_UID;

import static org.testng.AssertJUnit.*;
Expand All @@ -22,8 +23,6 @@
import com.evolveum.midpoint.test.TestResource;
import com.evolveum.midpoint.test.TestObject;

import com.evolveum.midpoint.util.exception.NotHereAssertionError;

import org.jetbrains.annotations.Nullable;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
Expand Down Expand Up @@ -1841,9 +1840,10 @@ public void test510StanDisablePosixAssocAndReconcile() throws Exception {

/*
Actually, stan is technically still a member of Rangers.
(Although not shown to midPoint, as he is no longer "posixAccount".)
This can be avoided by setting the associations as non-tolerant.
Stan is still a member of Rangers. Before 4.9, this was not shown by midPoint, as he is no longer "posixAccount",
but the membership is still there. In 4.9, the membership is shown, along with all other "unknown" memberships.
The membership can be removed by setting the association as non-tolerant.
attributes:
dn:
Expand Down Expand Up @@ -1941,7 +1941,7 @@ public void test560BigUidInbound() throws Exception {
protected void assertAccountTest510(PrismObject<ShadowType> shadow) throws Exception {
assertBasicAccount(shadow);

assertNoGroupAssociation(shadow, groupRangersOid);
assertGroupAssociation(shadow, groupRangersOid);
assertGroupAssociation(shadow, groupMonkeyIslandOid);
}

Expand Down Expand Up @@ -1984,13 +1984,13 @@ void assertUserPosix(PrismObject<UserType> user, String username, String firstNa
PrismAsserts.assertPropertyValue(extension, EXTENSION_UID_NUMBER_INT_NAME, uidNumber);
}

String assertBasicAccount(PrismObject<ShadowType> shadow) throws DirectoryException {
String assertBasicAccount(PrismObject<ShadowType> shadow) throws DirectoryException, SchemaException {
ShadowType shadowType = shadow.asObjectable();
assertEquals("Wrong objectclass in " + shadow, OPENDJ_ACCOUNT_STRUCTURAL_OBJECTCLASS_NAME, shadowType.getObjectClass());
assertTrue("Unexpected auxiliary objectclasses in " + shadow + ": " + shadowType.getAuxiliaryObjectClass(),
shadowType.getAuxiliaryObjectClass().isEmpty());
//noinspection ConstantConditions
String dn = (String) ShadowUtil.getSecondaryIdentifiers(shadow).iterator().next().getRealValue();
String dn = getDn(shadow);

Entry entry = openDJController.fetchEntry(dn);
assertNotNull("No ou LDAP entry for " + dn, entry);
Expand All @@ -2002,13 +2002,14 @@ String assertBasicAccount(PrismObject<ShadowType> shadow) throws DirectoryExcept
}

@SuppressWarnings("UnusedReturnValue")
String assertAccount(PrismObject<ShadowType> shadow, QName... expectedAuxObjectClasses) throws DirectoryException {
String assertAccount(PrismObject<ShadowType> shadow, QName... expectedAuxObjectClasses)
throws DirectoryException, SchemaException {
ShadowType shadowType = shadow.asObjectable();
assertEquals("Wrong objectclass in " + shadow, OPENDJ_ACCOUNT_STRUCTURAL_OBJECTCLASS_NAME, shadowType.getObjectClass());
PrismAsserts.assertEqualsCollectionUnordered("Wrong auxiliary objectclasses in " + shadow,
shadowType.getAuxiliaryObjectClass(), expectedAuxObjectClasses);
//noinspection ConstantConditions
String dn = (String) ShadowUtil.getSecondaryIdentifiers(shadow).iterator().next().getRealValue();
String dn = getDn(shadow);

Entry entry = openDJController.fetchEntry(dn);
assertNotNull("No ou LDAP entry for " + dn, entry);
Expand All @@ -2018,17 +2019,18 @@ String assertAccount(PrismObject<ShadowType> shadow, QName... expectedAuxObjectC
return entry.getDN().toString();
}

String assertPosixAccount(PrismObject<ShadowType> shadow, int expectedUid) throws DirectoryException {
String assertPosixAccount(PrismObject<ShadowType> shadow, int expectedUid) throws DirectoryException, SchemaException {
return assertPosixAccount(shadow, BigInteger.valueOf(expectedUid));
}

String assertPosixAccount(PrismObject<ShadowType> shadow, BigInteger expectedUid) throws DirectoryException {
String assertPosixAccount(PrismObject<ShadowType> shadow, BigInteger expectedUid)
throws DirectoryException, SchemaException {
ShadowType shadowType = shadow.asObjectable();
assertEquals("Wrong objectclass in " + shadow, OPENDJ_ACCOUNT_STRUCTURAL_OBJECTCLASS_NAME, shadowType.getObjectClass());
PrismAsserts.assertEqualsCollectionUnordered("Wrong auxiliary objectclasses in " + shadow,
shadowType.getAuxiliaryObjectClass(), OPENDJ_ACCOUNT_POSIX_AUXILIARY_OBJECTCLASS_NAME);
//noinspection ConstantConditions
String dn = (String) ShadowUtil.getSecondaryIdentifiers(shadow).iterator().next().getRealValue();
String dn = getDn(shadow);
if (expectedUid != null) {
ResourceAttribute<BigInteger> uidNumberAttr = ShadowUtil
.getAttribute(shadow, new QName(RESOURCE_OPENDJ_NAMESPACE, OPENDJ_UIDNUMBER_ATTRIBUTE_NAME));
Expand Down Expand Up @@ -2092,13 +2094,13 @@ private PrismObject<RoleType> createUnixGroupRole(String name, String metaRoleOi
return role;
}

private String assertLdapGroup(PrismObject<ShadowType> shadow) throws DirectoryException {
private String assertLdapGroup(PrismObject<ShadowType> shadow) throws DirectoryException, SchemaException {
ShadowType shadowType = shadow.asObjectable();
assertEquals("Wrong objectclass in " + shadow, OPENDJ_GROUP_STRUCTURAL_OBJECTCLASS_NAME, shadowType.getObjectClass());
assertTrue("Unexpected auxiliary objectclasses in " + shadow + ": " + shadowType.getAuxiliaryObjectClass(),
shadowType.getAuxiliaryObjectClass().isEmpty());
//noinspection ConstantConditions
String dn = (String) ShadowUtil.getSecondaryIdentifiers(shadow).iterator().next().getRealValue();
String dn = getDn(shadow);

Entry entry = openDJController.fetchEntry(dn);
assertNotNull("No group LDAP entry for " + dn, entry);
Expand All @@ -2109,13 +2111,14 @@ private String assertLdapGroup(PrismObject<ShadowType> shadow) throws DirectoryE
return entry.getDN().toString();
}

private String assertUnixGroup(PrismObject<ShadowType> shadow, Integer expectedGidNumber) throws DirectoryException {
private String assertUnixGroup(PrismObject<ShadowType> shadow, Integer expectedGidNumber)
throws DirectoryException, SchemaException {
ShadowType shadowType = shadow.asObjectable();
assertEquals("Wrong objectclass in " + shadow, OPENDJ_GROUP_UNIX_STRUCTURAL_OBJECTCLASS_NAME, shadowType.getObjectClass());
PrismAsserts.assertEqualsCollectionUnordered("Wrong auxiliary objectclasses in " + shadow,
shadowType.getAuxiliaryObjectClass(), OPENDJ_GROUP_POSIX_AUXILIARY_OBJECTCLASS_NAME);
//noinspection ConstantConditions
String dn = (String) ShadowUtil.getSecondaryIdentifiers(shadow).iterator().next().getRealValue();
String dn = getDn(shadow);
ResourceAttribute<BigInteger> gidNumberAttr = ShadowUtil.getAttribute(shadow, new QName(RESOURCE_OPENDJ_NAMESPACE, OPENDJ_GIDNUMBER_ATTRIBUTE_NAME));
PrismAsserts.assertPropertyValue(gidNumberAttr, BigInteger.valueOf(expectedGidNumber));

Expand Down Expand Up @@ -2201,4 +2204,8 @@ protected Long getTimestampAttribute(PrismObject<ShadowType> shadow) throws Exce
XMLGregorianCalendar attributeValue = ShadowUtil.getAttributeValue(shadow, OPENDJ_MODIFY_TIMESTAMP_ATTRIBUTE_QNAME);
return MiscUtil.asMillis(attributeValue);
}

static @Nullable String getDn(PrismObject<ShadowType> shadow) throws SchemaException {
return ShadowUtil.getAttributeValue(shadow, QNAME_DN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import java.util.Arrays;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.exception.SchemaException;

import org.apache.directory.api.util.GeneralizedTime;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
Expand Down Expand Up @@ -301,9 +303,9 @@ private void assertUserAuxes(PrismObject<UserType> userAfter, QName... expectedA
}

@SuppressWarnings("SameParameterValue")
private void assertLabeledUri(PrismObject<ShadowType> shadow, String expecteduri) throws DirectoryException {
private void assertLabeledUri(PrismObject<ShadowType> shadow, String expecteduri) throws DirectoryException, SchemaException {
//noinspection ConstantConditions
String dn = (String) ShadowUtil.getSecondaryIdentifiers(shadow).iterator().next().getRealValue();
String dn = getDn(shadow);

Entry entry = openDJController.fetchEntry(dn);
assertNotNull("No ou LDAP entry for " + dn, entry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
<attribute>
<ref>ri:uid</ref>
<matchingRule>mr:stringIgnoreCase</matchingRule>
<secondaryIdentifier>true</secondaryIdentifier>
<outbound>
<!-- This MUST be weak in case of OpenDJ. If DN (name) is changed then the uid will be changed
as a side-effect as it is a naming attribute. -->
Expand Down
1 change: 1 addition & 0 deletions testing/story/src/test/resources/unix/resource-opendj.xml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
<attribute>
<ref>ri:uid</ref>
<matchingRule>mr:stringIgnoreCase</matchingRule>
<secondaryIdentifier>true</secondaryIdentifier>
<outbound>
<!-- This MUST be weak in case of OpenDJ. If DN (name) is changed then the uid will be changed
as a side-effect as it is a naming attribute. -->
Expand Down

0 comments on commit 67c157d

Please sign in to comment.