Search in sources :

Example 1 with ResourceObjectOperations

use of com.evolveum.midpoint.provisioning.impl.ResourceObjectOperations in project midpoint by Evolveum.

the class EntitlementConverter method collectEntitlementAsObjectOperation.

private <TV, TA> PrismObject<ShadowType> collectEntitlementAsObjectOperation(Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap, PrismContainerValue<ShadowAssociationType> associationValue, PrismObject<ShadowType> subjectShadowBefore, PrismObject<ShadowType> subjectShadowAfter, ModificationType modificationType, ProvisioningContext subjectCtx, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
    ResourceType resource = subjectCtx.getResource();
    ShadowAssociationType associationBean = associationValue.asContainerable();
    QName associationName = associationBean.getName();
    if (associationName == null) {
        throw new SchemaException("No name in entitlement association " + associationValue);
    }
    ResourceObjectDefinition subjectDef = subjectCtx.getObjectDefinitionRequired();
    ResourceAssociationDefinition associationDef = subjectDef.findAssociationDefinitionRequired(associationName, () -> " in " + subjectCtx);
    ResourceObjectAssociationDirectionType direction = associationDef.getDirection();
    if (direction != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) {
        // Process just this one direction. The other direction was processed before
        return subjectShadowAfter;
    }
    Collection<String> entitlementIntents = associationDef.getIntents();
    if (entitlementIntents == null || entitlementIntents.isEmpty()) {
        throw new SchemaException("No entitlement intent specified in association " + associationValue + " in " + resource);
    }
    ShadowKindType entitlementKind = associationDef.getKind();
    for (String entitlementIntent : entitlementIntents) {
        // TODO deduplicate the code
        // todo error handling
        ProvisioningContext entitlementCtx = subjectCtx.spawnForKindIntent(entitlementKind, entitlementIntent);
        ResourceObjectDefinition entitlementOcDef = entitlementCtx.getObjectDefinitionRequired();
        QName assocAttrName = associationDef.getDefinitionBean().getAssociationAttribute();
        QName valueAttrName = associationDef.getDefinitionBean().getValueAttribute();
        schemaCheck(assocAttrName != null, "No association attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
        schemaCheck(valueAttrName != null, "No value attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
        // noinspection unchecked
        ResourceAttributeDefinition<TA> assocAttrDef = (ResourceAttributeDefinition<TA>) entitlementOcDef.findAttributeDefinition(assocAttrName);
        if (assocAttrDef == null) {
            throw new SchemaException("Association attribute '" + assocAttrName + "'defined in entitlement association " + "was not found in entitlement intent(s) '" + entitlementIntents + "' in schema for " + resource);
        }
        ResourceAttributeContainer identifiersContainer = getIdentifiersAttributeContainer(associationValue, entitlementOcDef);
        Collection<ResourceAttribute<?>> entitlementIdentifiersFromAssociation = identifiersContainer.getAttributes();
        ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), entitlementIdentifiersFromAssociation);
        ResourceObjectOperations operations = roMap.get(disc);
        if (operations == null) {
            operations = new ResourceObjectOperations();
            operations.setResourceObjectContext(entitlementCtx);
            roMap.put(disc, operations);
        }
        // Which shadow would we use - shadowBefore or shadowAfter?
        // 
        // If the operation is ADD or REPLACE, we use current version of the shadow (shadowAfter), because we want
        // to ensure that we add most-recent data to the subject.
        // 
        // If the operation is DELETE, we have two possibilities:
        // - if the resource provides referential integrity, the subject has already
        // new data (because the object operation was already carried out), so we use shadowAfter
        // - if the resource does not provide referential integrity, the subject has OLD data
        // so we use shadowBefore
        PrismObject<ShadowType> subjectShadow;
        if (modificationType != ModificationType.DELETE) {
            subjectShadow = subjectShadowAfter;
        } else {
            if (associationDef.requiresExplicitReferentialIntegrity()) {
                // we must ensure the referential integrity
                subjectShadow = subjectShadowBefore;
            } else {
                // i.e. resource has ref integrity assured by itself
                subjectShadow = subjectShadowAfter;
            }
        }
        ResourceAttribute<TV> valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName);
        if (valueAttr == null) {
            if (!ShadowUtil.isFullShadow(subjectShadow)) {
                Collection<ResourceAttribute<?>> subjectIdentifiers = ShadowUtil.getAllIdentifiers(subjectShadow);
                LOGGER.trace("Fetching {} ({})", subjectShadow, subjectIdentifiers);
                subjectShadow = resourceObjectReferenceResolver.fetchResourceObject(subjectCtx, subjectIdentifiers, null, subjectShadow, result);
                subjectShadowAfter = subjectShadow;
                valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName);
            }
            if (valueAttr == null) {
                LOGGER.error("No value attribute {} in shadow\n{}", valueAttrName, subjectShadow.debugDump());
                // TODO: check schema and try to fetch full shadow if necessary
                throw new SchemaException("No value attribute " + valueAttrName + " in " + subjectShadow);
            }
        }
        PropertyDelta<TA> attributeDelta = null;
        for (Operation operation : operations.getOperations()) {
            if (operation instanceof PropertyModificationOperation) {
                PropertyModificationOperation<?> propOp = (PropertyModificationOperation<?>) operation;
                if (propOp.getPropertyDelta().getElementName().equals(assocAttrName)) {
                    // noinspection unchecked
                    attributeDelta = (PropertyDelta<TA>) propOp.getPropertyDelta();
                }
            }
        }
        if (attributeDelta == null) {
            attributeDelta = assocAttrDef.createEmptyDelta(ItemPath.create(ShadowType.F_ATTRIBUTES, assocAttrName));
        }
        PrismProperty<TA> changedAssocAttr = PrismUtil.convertProperty(valueAttr, assocAttrDef);
        if (modificationType == ModificationType.ADD) {
            attributeDelta.addValuesToAdd(changedAssocAttr.getClonedValues());
        } else if (modificationType == ModificationType.DELETE) {
            attributeDelta.addValuesToDelete(changedAssocAttr.getClonedValues());
        } else if (modificationType == ModificationType.REPLACE) {
            // TODO: check if already exists
            attributeDelta.setValuesToReplace(changedAssocAttr.getClonedValues());
        }
        if (ResourceTypeUtil.isAvoidDuplicateValues(resource)) {
            PrismObject<ShadowType> currentObjectShadow = operations.getCurrentShadow();
            if (currentObjectShadow == null) {
                LOGGER.trace("Fetching entitlement shadow {} to avoid value duplication (intent={})", entitlementIdentifiersFromAssociation, entitlementIntent);
                currentObjectShadow = resourceObjectReferenceResolver.fetchResourceObject(entitlementCtx, entitlementIdentifiersFromAssociation, null, null, result);
                operations.setCurrentShadow(currentObjectShadow);
            }
            // TODO It seems that duplicate values are checked twice: once here and the second time
            // in ResourceObjectConverter.executeModify. Check that and fix if necessary.
            PropertyDelta<TA> attributeDeltaAfterNarrow = ProvisioningUtil.narrowPropertyDelta(attributeDelta, currentObjectShadow, associationDef.getMatchingRule(), matchingRuleRegistry);
            if (attributeDeltaAfterNarrow == null || attributeDeltaAfterNarrow.isEmpty()) {
                LOGGER.trace("Not collecting entitlement object operations ({}) association {}: " + "attribute delta is empty after narrow, orig delta: {}", modificationType, associationName.getLocalPart(), attributeDelta);
            }
            attributeDelta = attributeDeltaAfterNarrow;
        }
        if (attributeDelta != null && !attributeDelta.isEmpty()) {
            PropertyModificationOperation<?> attributeModification = new PropertyModificationOperation<>(attributeDelta);
            attributeModification.setMatchingRuleQName(associationDef.getMatchingRule());
            LOGGER.trace("Collecting entitlement object operations ({}) association {}: {}", modificationType, associationName.getLocalPart(), attributeModification);
            operations.add(attributeModification);
        }
    }
    return subjectShadowAfter;
}
Also used : ResourceObjectDiscriminator(com.evolveum.midpoint.provisioning.impl.ResourceObjectDiscriminator) QName(javax.xml.namespace.QName) ProvisioningContext(com.evolveum.midpoint.provisioning.impl.ProvisioningContext) ResourceObjectOperations(com.evolveum.midpoint.provisioning.impl.ResourceObjectOperations)

Example 2 with ResourceObjectOperations

use of com.evolveum.midpoint.provisioning.impl.ResourceObjectOperations in project midpoint by Evolveum.

the class EntitlementConverter method collectEntitlementsAsObjectOperationDelete.

// endregion
// region Delete
// ///////
// DELETE
// ///////
/**
 * This is somehow different that all the other methods. We are not following the content of a shadow or delta.
 * We are following the definitions. This is to avoid the need to read the object that is going to be deleted.
 * In fact, the object should not be there any more, but we still want to clean up entitlement membership
 * based on the information from the shadow.
 *
 * @param <T> Type of the association (referencing) attribute
 */
<T> void collectEntitlementsAsObjectOperationDelete(Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap, PrismObject<ShadowType> subjectShadow, ProvisioningContext subjectCtx, OperationResult result) throws SchemaException, CommunicationException, ObjectNotFoundException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
    ResourceObjectDefinition subjectDef = subjectCtx.getObjectDefinitionRequired();
    Collection<ResourceAssociationDefinition> associationDefs = subjectDef.getAssociationDefinitions();
    if (associationDefs.isEmpty()) {
        LOGGER.trace("No associations in deleted shadow, nothing to do");
        return;
    }
    ResourceAttributeContainer subjectAttributesContainer = ShadowUtil.getAttributesContainer(subjectShadow);
    for (ResourceAssociationDefinition associationDef : associationDefs) {
        if (associationDef.getDirection() != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) {
            // We can ignore these. They will die together with the object. No need to explicitly delete them.
            LOGGER.trace("Ignoring subject-to-object association in deleted shadow");
            continue;
        }
        if (!associationDef.requiresExplicitReferentialIntegrity()) {
            LOGGER.trace("Ignoring association in deleted shadow because it does not require explicit" + " referential integrity assurance");
            continue;
        }
        if (associationDef.getAuxiliaryObjectClass() != null && !subjectDef.hasAuxiliaryObjectClass(associationDef.getAuxiliaryObjectClass())) {
            LOGGER.trace("Ignoring association in deleted shadow because subject does not have {} auxiliary object class", associationDef.getAuxiliaryObjectClass());
            continue;
        }
        QName associationName = associationDef.getName();
        ShadowKindType entitlementKind = associationDef.getKind();
        for (String entitlementIntent : associationDef.getIntents()) {
            // TODO deduplicate the code
            // TODO error handling
            ProvisioningContext entitlementCtx = subjectCtx.spawnForKindIntent(entitlementKind, entitlementIntent);
            ResourceObjectDefinition entitlementDef = entitlementCtx.getObjectDefinitionRequired();
            QName assocAttrName = associationDef.getDefinitionBean().getAssociationAttribute();
            QName valueAttrName = associationDef.getDefinitionBean().getValueAttribute();
            schemaCheck(assocAttrName != null, "No association attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
            schemaCheck(valueAttrName != null, "No value attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
            // noinspection unchecked
            ResourceAttributeDefinition<T> assocAttrDef = (ResourceAttributeDefinition<T>) entitlementDef.findAttributeDefinitionRequired(assocAttrName, () -> " in entitlement association '" + associationName + "' in " + entitlementCtx + " [association attribute]");
            final ResourceAttribute<T> valueAttr = subjectAttributesContainer.findAttribute(valueAttrName);
            if (valueAttr == null || valueAttr.isEmpty()) {
                // Although we cannot really remedy the situation now, we at least throw an error so the problem is detected.
                throw new SchemaException("Value attribute " + valueAttrName + " has no value; attribute defined in entitlement " + "association '" + associationName + "' in " + subjectCtx);
            }
            if (valueAttr.size() > 1) {
                throw new SchemaException("Value attribute " + valueAttrName + " has no more than one value; attribute defined" + " in entitlement association '" + associationName + "' in " + subjectCtx);
            }
            ResourceAttributeDefinition<T> valueAttrDef = valueAttr.getDefinition();
            PrismPropertyValue<T> valueAttrValue = valueAttr.getAnyValue();
            ObjectQuery query = createEntitlementQuery(valueAttrValue, valueAttrDef, assocAttrDef, associationDef);
            AttributesToReturn attributesToReturn = ProvisioningUtil.createAttributesToReturn(entitlementCtx);
            SearchHierarchyConstraints searchHierarchyConstraints = determineSearchHierarchyConstraints(entitlementCtx, result);
            ObjectHandler handler = (ucfObject, lResult) -> {
                PrismObject<ShadowType> entitlementShadow = ucfObject.getResourceObject();
                Collection<? extends ResourceAttribute<?>> primaryIdentifiers = ShadowUtil.getPrimaryIdentifiers(entitlementShadow);
                ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementDef.getTypeName(), primaryIdentifiers);
                ResourceObjectOperations operations = roMap.get(disc);
                if (operations == null) {
                    operations = new ResourceObjectOperations();
                    roMap.put(disc, operations);
                    operations.setResourceObjectContext(entitlementCtx);
                    Collection<? extends ResourceAttribute<?>> allIdentifiers = ShadowUtil.getAllIdentifiers(entitlementShadow);
                    operations.setAllIdentifiers(allIdentifiers);
                }
                PropertyDelta<T> attributeDelta = null;
                for (Operation operation : operations.getOperations()) {
                    if (operation instanceof PropertyModificationOperation) {
                        PropertyModificationOperation<?> propOp = (PropertyModificationOperation<?>) operation;
                        if (propOp.getPropertyDelta().getElementName().equals(assocAttrName)) {
                            // noinspection unchecked
                            attributeDelta = (PropertyDelta<T>) propOp.getPropertyDelta();
                        }
                    }
                }
                if (attributeDelta == null) {
                    attributeDelta = assocAttrDef.createEmptyDelta(ItemPath.create(ShadowType.F_ATTRIBUTES, assocAttrName));
                    PropertyModificationOperation<?> attributeModification = new PropertyModificationOperation<>(attributeDelta);
                    attributeModification.setMatchingRuleQName(associationDef.getMatchingRule());
                    operations.add(attributeModification);
                }
                attributeDelta.addValuesToDelete(valueAttr.getClonedValues());
                LOGGER.trace("Association in deleted shadow delta:\n{}", attributeDelta.debugDumpLazily());
                return true;
            };
            try {
                LOGGER.trace("Searching for associations in deleted shadow, query: {}", query);
                ConnectorInstance connector = subjectCtx.getConnector(ReadCapabilityType.class, result);
                connector.search(entitlementDef, query, handler, attributesToReturn, null, searchHierarchyConstraints, UcfFetchErrorReportingMethod.EXCEPTION, subjectCtx.getUcfExecutionContext(), result);
            } catch (TunnelException e) {
                throw (SchemaException) e.getCause();
            } catch (GenericFrameworkException e) {
                throw new GenericConnectorException(e.getMessage(), e);
            }
        }
    }
}
Also used : ResourceObjectOperations(com.evolveum.midpoint.provisioning.impl.ResourceObjectOperations) com.evolveum.midpoint.xml.ns._public.common.common_3(com.evolveum.midpoint.xml.ns._public.common.common_3) OperationResult(com.evolveum.midpoint.schema.result.OperationResult) Autowired(org.springframework.beans.factory.annotation.Autowired) MiscUtil.emptyIfNull(com.evolveum.midpoint.util.MiscUtil.emptyIfNull) HashMap(java.util.HashMap) Trace(com.evolveum.midpoint.util.logging.Trace) com.evolveum.midpoint.util.exception(com.evolveum.midpoint.util.exception) CollectionUtils(org.apache.commons.collections4.CollectionUtils) ResourceObjectDiscriminator(com.evolveum.midpoint.provisioning.impl.ResourceObjectDiscriminator) GenericConnectorException(com.evolveum.midpoint.provisioning.api.GenericConnectorException) ProvisioningContext(com.evolveum.midpoint.provisioning.impl.ProvisioningContext) Map(java.util.Map) MiscUtil.schemaCheck(com.evolveum.midpoint.util.MiscUtil.schemaCheck) com.evolveum.midpoint.prism(com.evolveum.midpoint.prism) ContainerDelta(com.evolveum.midpoint.prism.delta.ContainerDelta) ObjectFactory(com.evolveum.midpoint.schema.processor.ObjectFactory) Collection(java.util.Collection) PrismUtil(com.evolveum.midpoint.prism.util.PrismUtil) ResourceTypeUtil(com.evolveum.midpoint.schema.util.ResourceTypeUtil) ResourceObjectTypeDefinition(com.evolveum.midpoint.schema.processor.ResourceObjectTypeDefinition) MatchingRuleRegistry(com.evolveum.midpoint.prism.match.MatchingRuleRegistry) ItemPath(com.evolveum.midpoint.prism.path.ItemPath) Nullable(org.jetbrains.annotations.Nullable) Component(org.springframework.stereotype.Component) MatchingRule(com.evolveum.midpoint.prism.match.MatchingRule) ItemName(com.evolveum.midpoint.prism.path.ItemName) com.evolveum.midpoint.provisioning.ucf.api(com.evolveum.midpoint.provisioning.ucf.api) com.evolveum.midpoint.schema.processor(com.evolveum.midpoint.schema.processor) ProvisioningUtil(com.evolveum.midpoint.provisioning.util.ProvisioningUtil) ReadCapabilityType(com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ReadCapabilityType) ShadowUtil(com.evolveum.midpoint.schema.util.ShadowUtil) PropertyDelta(com.evolveum.midpoint.prism.delta.PropertyDelta) QName(javax.xml.namespace.QName) ObjectQuery(com.evolveum.midpoint.prism.query.ObjectQuery) NotNull(org.jetbrains.annotations.NotNull) TraceManager(com.evolveum.midpoint.util.logging.TraceManager) ResourceObjectDiscriminator(com.evolveum.midpoint.provisioning.impl.ResourceObjectDiscriminator) GenericConnectorException(com.evolveum.midpoint.provisioning.api.GenericConnectorException) PropertyDelta(com.evolveum.midpoint.prism.delta.PropertyDelta) QName(javax.xml.namespace.QName) ObjectQuery(com.evolveum.midpoint.prism.query.ObjectQuery) ProvisioningContext(com.evolveum.midpoint.provisioning.impl.ProvisioningContext) ResourceObjectOperations(com.evolveum.midpoint.provisioning.impl.ResourceObjectOperations) Collection(java.util.Collection)

Aggregations

ProvisioningContext (com.evolveum.midpoint.provisioning.impl.ProvisioningContext)2 ResourceObjectDiscriminator (com.evolveum.midpoint.provisioning.impl.ResourceObjectDiscriminator)2 ResourceObjectOperations (com.evolveum.midpoint.provisioning.impl.ResourceObjectOperations)2 QName (javax.xml.namespace.QName)2 com.evolveum.midpoint.prism (com.evolveum.midpoint.prism)1 ContainerDelta (com.evolveum.midpoint.prism.delta.ContainerDelta)1 PropertyDelta (com.evolveum.midpoint.prism.delta.PropertyDelta)1 MatchingRule (com.evolveum.midpoint.prism.match.MatchingRule)1 MatchingRuleRegistry (com.evolveum.midpoint.prism.match.MatchingRuleRegistry)1 ItemName (com.evolveum.midpoint.prism.path.ItemName)1 ItemPath (com.evolveum.midpoint.prism.path.ItemPath)1 ObjectQuery (com.evolveum.midpoint.prism.query.ObjectQuery)1 PrismUtil (com.evolveum.midpoint.prism.util.PrismUtil)1 GenericConnectorException (com.evolveum.midpoint.provisioning.api.GenericConnectorException)1 com.evolveum.midpoint.provisioning.ucf.api (com.evolveum.midpoint.provisioning.ucf.api)1 ProvisioningUtil (com.evolveum.midpoint.provisioning.util.ProvisioningUtil)1 com.evolveum.midpoint.schema.processor (com.evolveum.midpoint.schema.processor)1 ObjectFactory (com.evolveum.midpoint.schema.processor.ObjectFactory)1 ResourceObjectTypeDefinition (com.evolveum.midpoint.schema.processor.ResourceObjectTypeDefinition)1 OperationResult (com.evolveum.midpoint.schema.result.OperationResult)1