Skip to content

Commit

Permalink
Do final touches necessary for shadow analysis
Browse files Browse the repository at this point in the history
These are (hopefully) final code clarifications needed to perform
repository shadow accesses in the provisioning module. There are
several movements regarding responsibilities, like for resolving
secondary to primary identifiers against the repo. Some methods were
renamed.
  • Loading branch information
mederly committed Nov 13, 2023
1 parent 460661f commit 8378497
Show file tree
Hide file tree
Showing 31 changed files with 585 additions and 505 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ default ResourceAssociationDefinition findAssociationDefinitionRequired(QName na
.collect(Collectors.toCollection(HashSet::new));
}

/**
* Returns all attributes that are used as targets for object-to-subject associations, i.e., attributes whose values
* are referenced from the entitlements. For example, the group (an entitlement) may list its members by their `uid`
* attribute. This method returns `uid` in such a case.
*
* The goal is to making sure such attributes are always cached, regardless of whether they are formally defined
* as identifiers.
*/
default @NotNull Collection<? extends QName> getAssociationValueAttributes() {
return getAssociationDefinitions().stream()
.filter(assocDef -> assocDef.isObjectToSubject())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
package com.evolveum.midpoint.schema.processor;

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

import org.jetbrains.annotations.NotNull;

/**
/**
Expand Down Expand Up @@ -52,6 +55,14 @@ default String getNativeAttributeName() {
return definition != null ? definition.getNativeAttributeName() : null;
}

/** Returns self to be usable in chained calls. */
default @NotNull ResourceAttribute<T> forceDefinitionFrom(ResourceObjectDefinition objectDefinition) throws SchemaException {
var attrDef = objectDefinition.findAttributeDefinitionRequired(getElementName());
//noinspection unchecked
applyDefinition((ResourceAttributeDefinition<T>) attrDef, true);
return this;
}

@Override
ResourceAttribute<T> clone();
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private ResourceObjectIdentification(
this.identifiers = identifiers;
}

public static ResourceObjectIdentification<?> of(
public static @NotNull ResourceObjectIdentification<?> of(
@NotNull ResourceObjectDefinition resourceObjectDefinition,
@NotNull ResourceObjectIdentifiers identifiers) {
if (identifiers instanceof ResourceObjectIdentifiers.WithPrimary withPrimary) {
Expand All @@ -74,6 +74,12 @@ public static ResourceObjectIdentification<?> of(
}
}

public static @NotNull ResourceObjectIdentification.WithPrimary of(
@NotNull ResourceObjectDefinition resourceObjectDefinition,
@NotNull ResourceObjectIdentifiers.WithPrimary primaryIdentifiers) {
return new WithPrimary(resourceObjectDefinition, primaryIdentifiers);
}

/** Creates new identification with a primary identifier. */
public static WithPrimary withPrimary(
@NotNull ResourceObjectDefinition resourceObjectDefinition,
Expand Down Expand Up @@ -118,7 +124,7 @@ public WithPrimary withPrimary(@NotNull ResourceObjectIdentifier.Primary<?> prim
}

/** See {@link ResourceObjectIdentifiers#of(Collection, Collection)} for preconditions. */
public static ResourceObjectIdentification<?> of(
public static @NotNull ResourceObjectIdentification<?> of(
@NotNull ResourceObjectDefinition resourceObjectDefinition,
@NotNull Collection<ResourceObjectIdentifier.Primary<?>> primaryIdentifiers,
@NotNull Collection<ResourceObjectIdentifier.Secondary<?>> secondaryIdentifiers) throws SchemaException {
Expand All @@ -127,7 +133,7 @@ public static ResourceObjectIdentification<?> of(
ResourceObjectIdentifiers.of(primaryIdentifiers, secondaryIdentifiers));
}

private static ResourceObjectIdentification<?> fromIdentifiersOrAttributes(
private static @NotNull ResourceObjectIdentification<?> fromIdentifiersOrAttributes(
@NotNull ResourceObjectDefinition objectDefinition,
@NotNull Collection<? extends ResourceAttribute<?>> allAttributes,
boolean nonIdentifiersAllowed) throws SchemaException {
Expand All @@ -149,13 +155,13 @@ private static ResourceObjectIdentification<?> fromIdentifiersOrAttributes(
return ResourceObjectIdentification.of(objectDefinition, primaryIdentifiers, secondaryIdentifiers);
}

public static ResourceObjectIdentification<?> fromIdentifiers(
public static @NotNull ResourceObjectIdentification<?> fromIdentifiers(
@NotNull ResourceObjectDefinition objectDefinition,
@NotNull Collection<? extends ResourceAttribute<?>> allIdentifiers) throws SchemaException {
return fromIdentifiersOrAttributes(objectDefinition, allIdentifiers, false);
}

public static ResourceObjectIdentification<?> fromAssociationValue(
public static @NotNull ResourceObjectIdentification<?> fromAssociationValue(
@NotNull ResourceObjectDefinition targetObjDef,
@NotNull PrismContainerValue<ShadowAssociationType> associationValue)
throws SchemaException {
Expand All @@ -164,7 +170,7 @@ public static ResourceObjectIdentification<?> fromAssociationValue(
getIdentifiersAttributes(associationValue, targetObjDef));
}

public static ResourceObjectIdentification<?> fromAttributes(
public static @NotNull ResourceObjectIdentification<?> fromAttributes(
@NotNull ResourceObjectDefinition resourceObjectDefinition,
@NotNull Collection<? extends ResourceAttribute<?>> attributes) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public abstract class ResourceObjectIdentifiers implements Serializable, DebugDu
() -> new IllegalStateException("No primary identifier in " + this));
}

/** Not empty for {@link SecondaryOnly} instances. */
public @NotNull Set<ResourceObjectIdentifier.Secondary<?>> getSecondaryIdentifiers() {
return secondaryIdentifiers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ public static Collection<ResourceAttribute<?>> getSecondaryIdentifiers(PrismObje
return attributesContainer.getSecondaryIdentifiers();
}

public static @Nullable Collection<ResourceAttribute<?>> getAllIdentifiers(PrismObject<? extends ShadowType> shadow) {
public static @NotNull Collection<ResourceAttribute<?>> getAllIdentifiers(PrismObject<? extends ShadowType> shadow) {
ResourceAttributeContainer attributesContainer = getAttributesContainer(shadow);
if (attributesContainer == null) {
return null;
return List.of();
}
return attributesContainer.getAllIdentifiers();
}

public static Collection<ResourceAttribute<?>> getAllIdentifiers(ShadowType shadow) {
public static @NotNull Collection<ResourceAttribute<?>> getAllIdentifiers(ShadowType shadow) {
return getAllIdentifiers(shadow.asPrismObject());
}

Expand Down Expand Up @@ -980,4 +980,11 @@ public static boolean isNonAttributeResourceModification(QName firstPathName) {
.max(Comparator.comparing(desc -> XmlTypeConverter.toMillis(desc.getTimestamp())))
.orElse(null);
}

public static @NotNull ResourceAttributeContainer getIdentifiersContainerRequired(
@NotNull PrismContainerValue<ShadowAssociationType> associationValue) throws SchemaException {
return MiscUtil.requireNonNull(
getAttributesContainer(associationValue, ShadowAssociationType.F_IDENTIFIERS),
() -> "No identifiers in " + associationValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,7 @@ private void toAssociation(PrismObject<ShadowType> shadow, ShadowAssociationType
ShadowAssociationType.F_IDENTIFIERS, shadowAttributesContainer.getDefinition());
//noinspection unchecked
shadowAssociationType.asPrismContainerValue().add(identifiersContainer);
Collection<ResourceAttribute<?>> shadowIdentifiers =
Objects.requireNonNull(ShadowUtil.getAllIdentifiers(shadow), "no shadow identifiers");
for (ResourceAttribute<?> shadowIdentifier : shadowIdentifiers) {
for (ResourceAttribute<?> shadowIdentifier : ShadowUtil.getAllIdentifiers(shadow)) {
identifiersContainer.add(shadowIdentifier.clone());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public void notifyEvent(ExternalResourceEvent event, Task task, OperationResult
ctx.assertDefinition();

Object primaryIdentifierRealValue = getPrimaryIdentifierRealValue(anyShadow, event);
Collection<ResourceAttribute<?>> identifiers = emptyIfNull(ShadowUtil.getAllIdentifiers(anyShadow));
Collection<ResourceAttribute<?>> identifiers = ShadowUtil.getAllIdentifiers(anyShadow);
if (identifiers.isEmpty()) {
throw new SchemaException("No identifiers");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package com.evolveum.midpoint.provisioning.impl;

import java.io.Serializable;
import java.util.Objects;
import javax.xml.namespace.QName;

import com.google.common.base.Preconditions;
Expand All @@ -17,16 +18,17 @@
import com.evolveum.midpoint.util.QNameUtil;

/**
* @param objectClass Qualified object class name.
* @param identifiers Originally, here we expected primary identifiers. But in fact, clients put here almost anything.
* - `objectClass`: Qualified object class name.
* - `identifiers`: Identifiers of the object that contain the primary identifier.
*
* @author semancik
*/
public record ResourceObjectDiscriminator(
@NotNull QName objectClass,
@NotNull ResourceObjectIdentifiers identifiers)
@NotNull ResourceObjectIdentifiers.WithPrimary identifiers)
implements Serializable {

public static @NotNull ResourceObjectDiscriminator of(@NotNull ResourceObjectIdentification<?> identification) {
public static @NotNull ResourceObjectDiscriminator of(@NotNull ResourceObjectIdentification.WithPrimary identification) {
return new ResourceObjectDiscriminator(
identification.getResourceObjectDefinition().getObjectClassName(),
identification.getIdentifiers());
Expand All @@ -37,6 +39,28 @@ public record ResourceObjectDiscriminator(
QNameUtil.isQualified(objectClass), "Object class must be qualified: %s", objectClass);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ResourceObjectDiscriminator that = (ResourceObjectDiscriminator) o;
return Objects.equals(objectClass, that.objectClass)
&& Objects.equals(getPrimaryIdentifierRealValue(), that.getPrimaryIdentifierRealValue());
}

@Override
public int hashCode() {
return Objects.hash(objectClass, getPrimaryIdentifierRealValue());
}

private @NotNull Object getPrimaryIdentifierRealValue() {
return identifiers.getPrimaryIdentifier().getRealValue();
}

@Override
public String toString() {
return "ResourceObjectDiscriminator(" + objectClass + ": " + identifiers + ")";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@
import java.util.Collection;

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

import com.evolveum.midpoint.provisioning.ucf.api.Operation;
import com.evolveum.midpoint.schema.processor.ResourceObjectIdentifiers;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;

/**
Expand All @@ -32,14 +30,8 @@ public class ResourceObjectOperations {
/** The context in which the operations will be carried out. */
@NotNull private final ProvisioningContext resourceObjectContext;

/** TODO */
@Nullable private final ResourceObjectIdentifiers.WithPrimary allIdentifiers;

public ResourceObjectOperations(
@NotNull ProvisioningContext resourceObjectContext,
@Nullable ResourceObjectIdentifiers.WithPrimary allIdentifiers) {
public ResourceObjectOperations(@NotNull ProvisioningContext resourceObjectContext) {
this.resourceObjectContext = resourceObjectContext;
this.allIdentifiers = allIdentifiers;
}

public ShadowType getCurrentShadow() {
Expand All @@ -64,10 +56,6 @@ public void add(@NotNull Operation operation) {
}
}

public @Nullable ResourceObjectIdentifiers.WithPrimary getAllIdentifiers() {
return allIdentifiers;
}

@Override
public String toString() {
return "ResourceObjectOperations("
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@

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

import com.evolveum.midpoint.provisioning.impl.ProvisioningContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.evolveum.midpoint.provisioning.impl.ProvisioningContext;
import com.evolveum.midpoint.provisioning.ucf.api.UcfFetchErrorReportingMethod;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FetchErrorReportingMethodType;

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

/**
* Either full-featured `search` or simpler `locate` or `fetch` operations.
*
* @see ResourceObjectSearchOperation
* @see ResourceObjectLocateOrFetchOperation
* @see ResourceObjectLocateOperation
*/
abstract class AbstractResourceObjectSearchOperation {
abstract class AbstractResourceObjectRetrievalOperation {

@NotNull final ProvisioningContext ctx;

Expand All @@ -32,7 +32,7 @@ abstract class AbstractResourceObjectSearchOperation {

@NotNull final ResourceObjectsBeans b = ResourceObjectsBeans.get();

AbstractResourceObjectSearchOperation(
AbstractResourceObjectRetrievalOperation(
@NotNull ProvisioningContext ctx,
boolean fetchAssociations,
@Nullable FetchErrorReportingMethodType errorReportingMethod) {
Expand All @@ -48,4 +48,15 @@ abstract class AbstractResourceObjectSearchOperation {
return UcfFetchErrorReportingMethod.EXCEPTION;
}
}

/**
* Does all the necessary processing at "resource objects" layer: activation, protected flag, associations, and so on.
*
* @see ResourceObjectFound#completeResourceObject(ProvisioningContext, ResourceObject, boolean, OperationResult)
*/
@NotNull CompleteResourceObject complete(@NotNull ResourceObject object, @NotNull OperationResult result) {
ResourceObjectFound objectFound = new ResourceObjectFound(object, ctx, fetchAssociations);
objectFound.initialize(ctx.getTask(), result);
return objectFound.asCompleteResourceObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,7 @@ ShadowType transformToObjectOpsOnModify(
ResourceObjectOperations singleObjectOperations =
objectsOperations.findOrCreate(
ResourceObjectDiscriminator.of(entitlementIdentification),
def.entitlementCtx,
entitlementIdentification.getIdentifiers());
def.entitlementCtx);

PropertyDelta<T> attributeDelta = null;
for (Operation ucfOperation: singleObjectOperations.getUcfOperations()) {
Expand Down Expand Up @@ -443,9 +442,10 @@ private <TV,TA> ShadowType collectObjectOps(
}

Collection<String> entitlementIntents = associationDef.getIntents();
if (entitlementIntents == null || entitlementIntents.isEmpty()) {
if (entitlementIntents.isEmpty()) {
throw new SchemaException("No entitlement intent specified in association " + associationValue + " in " + resource);
}
// TODO reconsider the effectiveness of executing this loop repeatedly for multiple intents
for (String entitlementIntent : entitlementIntents) {
ResolvedAssociationDefinition def = resolveDefinition(associationDef, entitlementIntent);

Expand All @@ -458,13 +458,16 @@ private <TV,TA> ShadowType collectObjectOps(
+ "'%s' in schema for %s").formatted(def.assocAttrName, entitlementIntents, resource));
}

ResourceObjectIdentification<?> entitlementIdentification =
ResourceObjectIdentification<?> rawEntitlementIdentification = // may be secondary-only
ResourceObjectIdentification.fromAssociationValue(def.entitlementObjDef, associationValue);
var primaryEntitlementIdentification =
b.resourceObjectReferenceResolver.resolvePrimaryIdentifier(
def.entitlementCtx, rawEntitlementIdentification, result);

ResourceObjectOperations objectOperations =
objectsOperations.findOrCreate(
ResourceObjectDiscriminator.of(entitlementIdentification),
def.entitlementCtx,
null);
ResourceObjectDiscriminator.of(primaryEntitlementIdentification),
def.entitlementCtx);

// Which shadow would we use - shadowBefore or shadowAfter?
//
Expand Down Expand Up @@ -496,7 +499,7 @@ private <TV,TA> ShadowType collectObjectOps(
subjectCtx.getIdentificationFromShadow(subjectShadow);
LOGGER.trace("Fetching {} ({})", subjectShadow, subjectIdentification);
subjectShadow = ResourceObject.getBean(
ResourceObjectLocateOrFetchOperation.executeFetchRaw( // TODO what if there is no read capability?
ResourceObjectFetchOperation.executeRaw( // TODO what if there is no read capability?
subjectCtx, subjectIdentification, subjectShadow, result));
subjectShadowAfter = subjectShadow;
valueAttr = ShadowUtil.getAttribute(subjectShadow, def.valueAttrName);
Expand Down Expand Up @@ -535,10 +538,10 @@ private <TV,TA> ShadowType collectObjectOps(
ShadowType currentObjectShadow = objectOperations.getCurrentShadow();
if (currentObjectShadow == null) {
LOGGER.trace("Fetching entitlement shadow {} to avoid value duplication (intent={})",
entitlementIdentification, entitlementIntent);
primaryEntitlementIdentification, entitlementIntent);
currentObjectShadow = ResourceObject.getBean(
ResourceObjectLocateOrFetchOperation.executeFetchRaw(
def.entitlementCtx, entitlementIdentification, null, result));
ResourceObjectFetchOperation.executeRaw(
def.entitlementCtx, primaryEntitlementIdentification, null, result));
objectOperations.setCurrentShadow(currentObjectShadow);
}
// TODO It seems that duplicate values are checked twice: once here and the second time
Expand Down Expand Up @@ -588,14 +591,12 @@ static class EntitlementObjectsOperations implements DebugDumpable {
final Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap = new HashMap<>();

@NotNull ResourceObjectOperations findOrCreate(
@NotNull ResourceObjectDiscriminator disc,
@NotNull ProvisioningContext entitlementCtx,
@Nullable ResourceObjectIdentifiers.WithPrimary entitlementsIdentifiers) {
@NotNull ResourceObjectDiscriminator disc, @NotNull ProvisioningContext entitlementCtx) {
ResourceObjectOperations existing = roMap.get(disc);
if (existing != null) {
return existing;
}
var operations = new ResourceObjectOperations(entitlementCtx, entitlementsIdentifiers);
var operations = new ResourceObjectOperations(entitlementCtx);
roMap.put(disc, operations);
return operations;
}
Expand Down

0 comments on commit 8378497

Please sign in to comment.