use of com.evolveum.midpoint.provisioning.api.GenericConnectorException in project midpoint by Evolveum.
the class ResourceObjectReferenceResolver method fetchResourceObject.
public PrismObject<ShadowType> fetchResourceObject(ProvisioningContext ctx, Collection<? extends ResourceAttribute<?>> identifiers, AttributesToReturn attributesToReturn, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
ResourceType resource = ctx.getResource();
ConnectorInstance connector = ctx.getConnector(ReadCapabilityType.class, parentResult);
RefinedObjectClassDefinition objectClassDefinition = ctx.getObjectClassDefinition();
try {
if (!ResourceTypeUtil.isReadCapabilityEnabled(resource)) {
throw new UnsupportedOperationException("Resource does not support 'read' operation");
}
ResourceObjectIdentification identification = ResourceObjectIdentification.create(objectClassDefinition, identifiers);
identification = resolvePrimaryIdentifiers(ctx, identification, parentResult);
identification.validatePrimaryIdenfiers();
return connector.fetchObject(ShadowType.class, identification, attributesToReturn, ctx, parentResult);
} catch (ObjectNotFoundException e) {
parentResult.recordFatalError("Object not found. Identifiers: " + identifiers + ". Reason: " + e.getMessage(), e);
throw new ObjectNotFoundException("Object not found. identifiers=" + identifiers + ", objectclass=" + PrettyPrinter.prettyPrint(objectClassDefinition.getTypeName()) + ": " + e.getMessage(), e);
} catch (CommunicationException e) {
parentResult.recordFatalError("Error communication with the connector " + connector + ": " + e.getMessage(), e);
throw e;
} catch (GenericFrameworkException e) {
parentResult.recordFatalError("Generic error in the connector " + connector + ". Reason: " + e.getMessage(), e);
throw new GenericConnectorException("Generic error in the connector " + connector + ". Reason: " + e.getMessage(), e);
} catch (SchemaException ex) {
parentResult.recordFatalError("Can't get resource object, schema error: " + ex.getMessage(), ex);
throw ex;
} catch (ExpressionEvaluationException ex) {
parentResult.recordFatalError("Can't get resource object, expression error: " + ex.getMessage(), ex);
throw ex;
} catch (ConfigurationException e) {
parentResult.recordFatalError(e);
throw e;
}
}
use of com.evolveum.midpoint.provisioning.api.GenericConnectorException in project midpoint by Evolveum.
the class EntitlementConverter method postProcessEntitlementEntitlementToSubject.
private <S extends ShadowType, T> void postProcessEntitlementEntitlementToSubject(ProvisioningContext subjectCtx, final PrismObject<S> resourceObject, RefinedAssociationDefinition assocDefType, final ProvisioningContext entitlementCtx, ResourceAttributeContainer attributesContainer, final PrismContainer<ShadowAssociationType> associationContainer, OperationResult parentResult) throws SchemaException, CommunicationException, ObjectNotFoundException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
ResourceType resourceType = subjectCtx.getResource();
final QName associationName = assocDefType.getName();
final RefinedObjectClassDefinition entitlementDef = entitlementCtx.getObjectClassDefinition();
if (associationName == null) {
throw new SchemaException("No name in entitlement association " + assocDefType + " in " + resourceType);
}
QName associationAuxiliaryObjectClass = assocDefType.getAuxiliaryObjectClass();
if (associationAuxiliaryObjectClass != null && associationAuxiliaryObjectClass.getNamespaceURI() != null && !associationAuxiliaryObjectClass.getNamespaceURI().equals(ResourceTypeUtil.getResourceNamespace(resourceType))) {
LOGGER.warn("Auxiliary object class {} in association {} does not have namespace that matches {}", associationAuxiliaryObjectClass, assocDefType.getName(), resourceType);
}
if (associationAuxiliaryObjectClass != null && !subjectCtx.getObjectClassDefinition().hasAuxiliaryObjectClass(associationAuxiliaryObjectClass)) {
LOGGER.trace("Ignoring association {} because subject does not have auxiliary object class {}, it has {}", associationName, associationAuxiliaryObjectClass, subjectCtx.getObjectClassDefinition().getAuxiliaryObjectClassDefinitions());
return;
}
QName assocAttrName = assocDefType.getResourceObjectAssociationType().getAssociationAttribute();
if (assocAttrName == null) {
throw new SchemaException("No association attribute defined in entitlement association '" + associationName + "' in " + resourceType);
}
RefinedAttributeDefinition assocAttrDef = entitlementDef.findAttributeDefinition(assocAttrName);
if (assocAttrDef == null) {
throw new SchemaException("Association attribute '" + assocAttrName + "'defined in entitlement association '" + associationName + "' was not found in schema for " + resourceType);
}
QName valueAttrName = assocDefType.getResourceObjectAssociationType().getValueAttribute();
if (valueAttrName == null) {
throw new SchemaException("No value attribute defined in entitlement association '" + associationName + "' in " + resourceType);
}
ResourceAttribute<T> valueAttr = attributesContainer.findAttribute(valueAttrName);
if (valueAttr == null || valueAttr.isEmpty()) {
LOGGER.trace("Ignoring association {} because subject does not have any value in attribute {}", associationName, valueAttrName);
return;
}
if (valueAttr.size() > 1) {
throw new SchemaException("Value attribute " + valueAttrName + " has no more than one value; attribute defined in entitlement association '" + associationName + "' in " + resourceType);
}
ObjectQuery query = createQuery(assocDefType, assocAttrDef, valueAttr);
AttributesToReturn attributesToReturn = ProvisioningUtil.createAttributesToReturn(entitlementCtx);
SearchHierarchyConstraints searchHierarchyConstraints = null;
ResourceObjectReferenceType baseContextRef = entitlementDef.getBaseContext();
if (baseContextRef != null) {
// TODO: this should be done once per search. Not in every run of postProcessEntitlementEntitlementToSubject
// this has to go outside of this method
PrismObject<ShadowType> baseContextShadow = resourceObjectReferenceResolver.resolve(subjectCtx, baseContextRef, null, "base context specification in " + entitlementDef, parentResult);
RefinedObjectClassDefinition baseContextObjectClassDefinition = subjectCtx.getRefinedSchema().determineCompositeObjectClassDefinition(baseContextShadow);
ResourceObjectIdentification baseContextIdentification = ShadowUtil.getResourceObjectIdentification(baseContextShadow, baseContextObjectClassDefinition);
searchHierarchyConstraints = new SearchHierarchyConstraints(baseContextIdentification, null);
}
ResultHandler<ShadowType> handler = new ResultHandler<ShadowType>() {
@Override
public boolean handle(PrismObject<ShadowType> entitlementShadow) {
PrismContainerValue<ShadowAssociationType> associationCVal = associationContainer.createNewValue();
associationCVal.asContainerable().setName(associationName);
Collection<ResourceAttribute<?>> entitlementIdentifiers = ShadowUtil.getAllIdentifiers(entitlementShadow);
try {
ResourceAttributeContainer identifiersContainer = new ResourceAttributeContainer(ShadowAssociationType.F_IDENTIFIERS, entitlementDef.toResourceAttributeContainerDefinition(), prismContext);
associationCVal.add(identifiersContainer);
identifiersContainer.getValue().addAll(ResourceAttribute.cloneCollection(entitlementIdentifiers));
// Remember the full shadow in user data. This is used later as an optimization to create the shadow in repo
identifiersContainer.setUserData(ResourceObjectConverter.FULL_SHADOW_KEY, entitlementShadow);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processed entitlement-to-subject association for account {} and entitlement {}", ShadowUtil.getHumanReadableName(resourceObject), ShadowUtil.getHumanReadableName(entitlementShadow));
}
} catch (SchemaException e) {
throw new TunnelException(e);
}
return true;
}
};
ConnectorInstance connector = subjectCtx.getConnector(ReadCapabilityType.class, parentResult);
try {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processed entitlement-to-subject association for account {}: query {}", ShadowUtil.getHumanReadableName(resourceObject), query);
}
try {
connector.search(entitlementDef, query, handler, attributesToReturn, null, searchHierarchyConstraints, subjectCtx, parentResult);
} catch (GenericFrameworkException e) {
throw new GenericConnectorException("Generic error in the connector " + connector + ". Reason: " + e.getMessage(), e);
}
} catch (TunnelException e) {
throw (SchemaException) e.getCause();
}
}
use of com.evolveum.midpoint.provisioning.api.GenericConnectorException in project midpoint by Evolveum.
the class ProvisioningServiceImpl method synchronize.
@SuppressWarnings("rawtypes")
@Override
public int synchronize(ResourceShadowDiscriminator shadowCoordinates, Task task, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
Validate.notNull(shadowCoordinates, "Coordinates oid must not be null.");
String resourceOid = shadowCoordinates.getResourceOid();
Validate.notNull(resourceOid, "Resource oid must not be null.");
Validate.notNull(task, "Task must not be null.");
Validate.notNull(parentResult, "Operation result must not be null.");
OperationResult result = parentResult.createSubresult(ProvisioningService.class.getName() + ".synchronize");
result.addParam(OperationResult.PARAM_OID, resourceOid);
result.addParam(OperationResult.PARAM_TASK, task.toString());
int processedChanges = 0;
try {
// Resolve resource
PrismObject<ResourceType> resource = getObject(ResourceType.class, resourceOid, null, task, result);
ResourceType resourceType = resource.asObjectable();
LOGGER.trace("**PROVISIONING: Start synchronization of resource {} ", resourceType);
// getting token form task
PrismProperty tokenProperty = getTokenProperty(shadowCoordinates, task, result);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("**PROVISIONING: Got token property: {} from the task extension.", SchemaDebugUtil.prettyPrint(tokenProperty));
}
processedChanges = getShadowCache(Mode.STANDARD).synchronize(shadowCoordinates, tokenProperty, task, result);
LOGGER.debug("Synchronization of {} done, token {}, {} changes", resource, tokenProperty, processedChanges);
} catch (ObjectNotFoundException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: object not found: " + e.getMessage(), e);
throw e;
} catch (CommunicationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: communication problem: " + e.getMessage(), e);
throw e;
} catch (ObjectAlreadyExistsException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: object already exists problem: " + e.getMessage(), e);
throw new SystemException(e);
} catch (GenericFrameworkException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: generic connector framework error: " + e.getMessage(), e);
throw new GenericConnectorException(e.getMessage(), e);
} catch (SchemaException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: schema problem: " + e.getMessage(), e);
throw e;
} catch (SecurityViolationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: security violation: " + e.getMessage(), e);
throw e;
} catch (ConfigurationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: configuration problem: " + e.getMessage(), e);
throw e;
} catch (RuntimeException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: unexpected problem: " + e.getMessage(), e);
throw e;
} catch (ExpressionEvaluationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: expression error: " + e.getMessage(), e);
throw e;
}
result.recordSuccess();
result.cleanupResult();
return processedChanges;
}
use of com.evolveum.midpoint.provisioning.api.GenericConnectorException in project midpoint by Evolveum.
the class ResourceObjectConverter method deleteResourceObject.
public AsynchronousOperationResult deleteResourceObject(ProvisioningContext ctx, PrismObject<ShadowType> shadow, OperationProvisioningScriptsType scripts, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
OperationResult result = parentResult.createSubresult(OPERATION_DELETE_RESOURCE_OBJECT);
LOGGER.trace("Deleting resource object {}", shadow);
Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil.getAllIdentifiers(shadow);
if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), shadow, matchingRuleRegistry)) {
LOGGER.error("Attempt to delete protected resource object " + ctx.getObjectClassDefinition() + ": " + identifiers + "; ignoring the request");
SecurityViolationException e = new SecurityViolationException("Cannot delete protected resource object " + ctx.getObjectClassDefinition() + ": " + identifiers);
result.recordFatalError(e);
throw e;
}
//check idetifier if it is not null
if (identifiers.isEmpty() && shadow.asObjectable().getFailedOperationType() != null) {
throw new GenericConnectorException("Unable to delete object from the resource. Probably it has not been created yet because of previous unavailability of the resource.");
}
// Execute entitlement modification on other objects (if needed)
executeEntitlementChangesDelete(ctx, shadow, scripts, result);
Collection<Operation> additionalOperations = new ArrayList<Operation>();
addExecuteScriptOperation(additionalOperations, ProvisioningOperationTypeType.DELETE, scripts, ctx.getResource(), result);
ConnectorInstance connector = ctx.getConnector(DeleteCapabilityType.class, result);
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PROVISIONING DELETE operation on {}\n DELETE object, object class {}, identified by:\n{}\n additional operations:\n{}", ctx.getResource(), shadow.asObjectable().getObjectClass(), SchemaDebugUtil.debugDump(identifiers), SchemaDebugUtil.debugDump(additionalOperations));
}
if (!ResourceTypeUtil.isDeleteCapabilityEnabled(ctx.getResource())) {
UnsupportedOperationException e = new UnsupportedOperationException("Resource does not support 'delete' operation");
result.recordFatalError(e);
throw e;
}
connector.deleteObject(ctx.getObjectClassDefinition(), additionalOperations, identifiers, ctx, result);
computeResultStatus(result);
LOGGER.debug("PROVISIONING DELETE: {}", result.getStatus());
} catch (ObjectNotFoundException ex) {
result.recordFatalError("Can't delete object " + shadow + ". Reason: " + ex.getMessage(), ex);
throw new ObjectNotFoundException("An error occured while deleting resource object " + shadow + "whith identifiers " + identifiers + ": " + ex.getMessage(), ex);
} catch (CommunicationException ex) {
result.recordFatalError("Error communicating with the connector " + connector + ": " + ex.getMessage(), ex);
throw new CommunicationException("Error communicating with the connector " + connector + ": " + ex.getMessage(), ex);
} catch (ConfigurationException ex) {
result.recordFatalError("Configuration error in connector " + connector + ": " + ex.getMessage(), ex);
throw new ConfigurationException("Configuration error in connector " + connector + ": " + ex.getMessage(), ex);
} catch (ExpressionEvaluationException ex) {
result.recordFatalError("Expression error while setting up the resource: " + ex.getMessage(), ex);
throw new ExpressionEvaluationException("Expression error while setting up the resource: " + ex.getMessage(), ex);
} catch (GenericFrameworkException ex) {
result.recordFatalError("Generic error in connector: " + ex.getMessage(), ex);
throw new GenericConnectorException("Generic error in connector: " + ex.getMessage(), ex);
}
LOGGER.trace("Deleted resource object {}", shadow);
return AsynchronousOperationResult.wrap(result);
}
use of com.evolveum.midpoint.provisioning.api.GenericConnectorException in project midpoint by Evolveum.
the class ResourceObjectConverter method modifyResourceObject.
public AsynchronousOperationReturnValue<Collection<PropertyDelta<PrismPropertyValue>>> modifyResourceObject(ProvisioningContext ctx, PrismObject<ShadowType> repoShadow, OperationProvisioningScriptsType scripts, Collection<? extends ItemDelta> itemDeltas, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException {
OperationResult result = parentResult.createSubresult(OPERATION_MODIFY_RESOURCE_OBJECT);
try {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Modifying resource object {}, deltas:\n", repoShadow, DebugUtil.debugDump(itemDeltas, 1));
}
RefinedObjectClassDefinition objectClassDefinition = ctx.getObjectClassDefinition();
Collection<Operation> operations = new ArrayList<Operation>();
Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil.getAllIdentifiers(repoShadow);
Collection<? extends ResourceAttribute<?>> primaryIdentifiers = ShadowUtil.getPrimaryIdentifiers(repoShadow);
if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), repoShadow, matchingRuleRegistry)) {
if (hasChangesOnResource(itemDeltas)) {
LOGGER.error("Attempt to modify protected resource object " + objectClassDefinition + ": " + identifiers);
SecurityViolationException e = new SecurityViolationException("Cannot modify protected resource object " + objectClassDefinition + ": " + identifiers);
result.recordFatalError(e);
throw e;
} else {
// Return immediately. This structure of the code makes sure that we do not execute any
// resource operation for protected account even if there is a bug in the code below.
LOGGER.trace("No resource modifications for protected resource object {}: {}; skipping", objectClassDefinition, identifiers);
result.recordNotApplicableIfUnknown();
return AsynchronousOperationReturnValue.wrap(null, result);
}
}
boolean hasVolatilityTriggerModification = false;
boolean hasResourceModification = false;
for (ItemDelta modification : itemDeltas) {
ItemPath path = modification.getPath();
QName firstPathName = ItemPath.getFirstName(path);
if (QNameUtil.match(firstPathName, ShadowType.F_ATTRIBUTES)) {
hasResourceModification = true;
QName attrName = ItemPath.getFirstName(path.rest());
RefinedAttributeDefinition<Object> attrDef = ctx.getObjectClassDefinition().findAttributeDefinition(attrName);
if (attrDef.isVolatilityTrigger()) {
LOGGER.trace("Will pre-read and re-read object because volatility trigger attribute {} has changed", attrName);
hasVolatilityTriggerModification = true;
break;
}
} else if (QNameUtil.match(firstPathName, ShadowType.F_ACTIVATION) || QNameUtil.match(firstPathName, ShadowType.F_CREDENTIALS) || QNameUtil.match(firstPathName, ShadowType.F_ASSOCIATION) || QNameUtil.match(firstPathName, ShadowType.F_AUXILIARY_OBJECT_CLASS)) {
hasResourceModification = true;
}
}
if (!hasResourceModification) {
// Quit early, so we avoid potential pre-read and other processing when there is no point of doing so.
// Also the read may fail which may invoke consistency mechanism which will complicate the situation.
LOGGER.trace("No resource modification found for {}, skipping", identifiers);
result.recordNotApplicableIfUnknown();
return AsynchronousOperationReturnValue.wrap(null, result);
}
/*
* State of the shadow before execution of the deltas - e.g. with original attributes, as it may be recorded in such a way in
* groups of which this account is a member of. (In case of object->subject associations.)
*
* This is used when the resource does NOT provide referential integrity by itself. This is e.g. the case of OpenDJ with default
* settings.
*
* On the contrary, AD and OpenDJ with referential integrity plugin do provide automatic referential integrity, so this feature is
* not needed.
*
* We decide based on setting of explicitReferentialIntegrity in association definition.
*/
collectAttributeAndEntitlementChanges(ctx, itemDeltas, operations, repoShadow, result);
PrismObject<ShadowType> preReadShadow = null;
Collection<PropertyModificationOperation> sideEffectOperations = null;
//check identifier if it is not null
if (primaryIdentifiers.isEmpty() && repoShadow.asObjectable().getFailedOperationType() != null) {
GenericConnectorException e = new GenericConnectorException("Unable to modify object in the resource. Probably it has not been created yet because of previous unavailability of the resource.");
result.recordFatalError(e);
throw e;
}
if (hasVolatilityTriggerModification || ResourceTypeUtil.isAvoidDuplicateValues(ctx.getResource()) || isRename(ctx, operations)) {
// We need to filter out the deltas that add duplicate values or remove values that are not there
LOGGER.trace("Pre-reading resource shadow");
// yes, we need associations here
preReadShadow = preReadShadow(ctx, identifiers, operations, true, result);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Pre-read object:\n{}", preReadShadow.debugDump());
}
}
if (!operations.isEmpty()) {
// This must go after the skip check above. Otherwise the scripts would be executed even if there is no need to.
addExecuteScriptOperation(operations, ProvisioningOperationTypeType.MODIFY, scripts, ctx.getResource(), result);
// Execute primary ICF operation on this shadow
sideEffectOperations = executeModify(ctx, preReadShadow, identifiers, operations, result);
} else {
// We have to check BEFORE we add script operations, otherwise the check would be pointless
LOGGER.trace("No modifications for connector object specified. Skipping processing of subject executeModify.");
}
Collection<PropertyDelta<PrismPropertyValue>> sideEffectDeltas = convertToPropertyDelta(sideEffectOperations);
/*
* State of the shadow after execution of the deltas - e.g. with new DN (if it was part of the delta), because this one should be recorded
* in groups of which this account is a member of. (In case of object->subject associations.)
*/
PrismObject<ShadowType> shadowAfter = preReadShadow == null ? repoShadow.clone() : preReadShadow.clone();
for (ItemDelta itemDelta : itemDeltas) {
itemDelta.applyTo(shadowAfter);
}
PrismObject<ShadowType> postReadShadow = null;
if (hasVolatilityTriggerModification) {
// There may be other changes that were not detected by the connector. Re-read the object and compare.
LOGGER.trace("Post-reading resource shadow");
postReadShadow = preReadShadow(ctx, identifiers, operations, true, result);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Post-read object:\n{}", postReadShadow.debugDump());
}
ObjectDelta<ShadowType> resourceShadowDelta = preReadShadow.diff(postReadShadow);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Determined side-effect changes by old-new diff:\n{}", resourceShadowDelta.debugDump());
}
for (ItemDelta modification : resourceShadowDelta.getModifications()) {
if (modification.getParentPath().startsWithName(ShadowType.F_ATTRIBUTES) && !ItemDelta.hasEquivalent(itemDeltas, modification)) {
ItemDelta.merge(sideEffectDeltas, modification);
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Side-effect changes after merging with old-new diff:\n{}", DebugUtil.debugDump(sideEffectDeltas));
}
}
Collection<? extends ItemDelta> allDeltas = new ArrayList<>();
((Collection) allDeltas).addAll(itemDeltas);
((Collection) allDeltas).addAll(sideEffectDeltas);
// Execute entitlement modification on other objects (if needed)
shadowAfter = executeEntitlementChangesModify(ctx, preReadShadow == null ? repoShadow : preReadShadow, postReadShadow == null ? shadowAfter : postReadShadow, scripts, allDeltas, result);
if (!sideEffectDeltas.isEmpty()) {
if (preReadShadow != null) {
PrismUtil.setDeltaOldValue(preReadShadow, sideEffectDeltas);
} else {
PrismUtil.setDeltaOldValue(repoShadow, sideEffectDeltas);
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Modificaiton side-effect changes:\n{}", DebugUtil.debugDump(sideEffectDeltas));
}
LOGGER.trace("Modified resource object {}", repoShadow);
computeResultStatus(result);
return AsynchronousOperationReturnValue.wrap(sideEffectDeltas, result);
} catch (Throwable e) {
result.recordFatalError(e);
throw e;
}
}
Aggregations