/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.provisioning.util; import com.evolveum.midpoint.common.ResourceObjectPattern; import com.evolveum.midpoint.common.StaticExpressionUtil; import com.evolveum.midpoint.common.refinery.*; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.match.MatchingRule; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.provisioning.impl.ConnectorSpec; import com.evolveum.midpoint.provisioning.impl.ProvisioningContext; import com.evolveum.midpoint.provisioning.ucf.api.AttributesToReturn; import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation; import com.evolveum.midpoint.provisioning.ucf.api.ExecuteScriptArgument; import com.evolveum.midpoint.schema.CapabilityUtil; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.processor.ResourceAttribute; import com.evolveum.midpoint.schema.processor.ResourceAttributeContainer; import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AttributeFetchStrategyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CachingPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CachingStategyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorInstanceSpecificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionReturnMultiplicityType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FailedOperationTypeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptArgumentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptHostType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningScriptType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectAssociationDirectionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ReadCapabilityType; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import org.apache.commons.lang.StringUtils; import java.util.ArrayList; import java.util.Collection; public class ProvisioningUtil { private static final QName FAKE_SCRIPT_ARGUMENT_NAME = new QName(SchemaConstants.NS_C, "arg"); private static final Trace LOGGER = TraceManager.getTrace(ProvisioningUtil.class); public static <T extends ShadowType> void normalizeShadow(T shadow, OperationResult result) throws SchemaException { if (shadow.getAttemptNumber() != null) { shadow.setAttemptNumber(null); } if (shadow.getFailedOperationType() != null) { shadow.setFailedOperationType(null); } if (shadow.getObjectChange() != null) { shadow.setObjectChange(null); } if (shadow.getResult() != null) { shadow.setResult(null); } if (shadow.getCredentials() != null) { shadow.setCredentials(null); } ResourceAttributeContainer normalizedContainer = ShadowUtil.getAttributesContainer(shadow); ResourceAttributeContainer oldContainer = normalizedContainer.clone(); normalizedContainer.clear(); Collection<ResourceAttribute<?>> identifiers = oldContainer.getPrimaryIdentifiers(); for (PrismProperty<?> p : identifiers) { normalizedContainer.getValue().add(p.clone()); } Collection<ResourceAttribute<?>> secondaryIdentifiers = oldContainer.getSecondaryIdentifiers(); for (PrismProperty<?> p : secondaryIdentifiers) { normalizedContainer.getValue().add(p.clone()); } cleanupShadowActivation(shadow); } public static PrismObjectDefinition<ShadowType> getResourceObjectShadowDefinition( PrismContext prismContext) { return prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(ShadowType.class); } public static ExecuteProvisioningScriptOperation convertToScriptOperation( ProvisioningScriptType scriptType, String desc, PrismContext prismContext) throws SchemaException { ExecuteProvisioningScriptOperation scriptOperation = new ExecuteProvisioningScriptOperation(); PrismPropertyDefinition scriptArgumentDefinition = new PrismPropertyDefinitionImpl( FAKE_SCRIPT_ARGUMENT_NAME, DOMUtil.XSD_STRING, prismContext); for (ProvisioningScriptArgumentType argument : scriptType.getArgument()) { ExecuteScriptArgument arg = new ExecuteScriptArgument(argument.getName(), StaticExpressionUtil.getStaticOutput(argument, scriptArgumentDefinition, desc, ExpressionReturnMultiplicityType.SINGLE, prismContext)); scriptOperation.getArgument().add(arg); } scriptOperation.setLanguage(scriptType.getLanguage()); scriptOperation.setTextCode(scriptType.getCode()); if (scriptType.getHost() != null && scriptType.getHost().equals(ProvisioningScriptHostType.CONNECTOR)) { scriptOperation.setConnectorHost(true); scriptOperation.setResourceHost(false); } if (scriptType.getHost() == null || scriptType.getHost().equals(ProvisioningScriptHostType.RESOURCE)) { scriptOperation.setConnectorHost(false); scriptOperation.setResourceHost(true); } return scriptOperation; } public static AttributesToReturn createAttributesToReturn(ProvisioningContext ctx) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { RefinedObjectClassDefinition objectClassDefinition = ctx.getObjectClassDefinition(); ResourceType resource = ctx.getResource(); boolean apply = false; AttributesToReturn attributesToReturn = new AttributesToReturn(); // Attributes boolean hasMinimal = false; for (RefinedAttributeDefinition attributeDefinition : objectClassDefinition.getAttributeDefinitions()) { if (attributeDefinition.getFetchStrategy() == AttributeFetchStrategyType.MINIMAL) { hasMinimal = true; break; } } attributesToReturn.setReturnDefaultAttributes(!hasMinimal); Collection<ResourceAttributeDefinition> explicit = new ArrayList<ResourceAttributeDefinition>(); for (RefinedAttributeDefinition attributeDefinition : objectClassDefinition.getAttributeDefinitions()) { AttributeFetchStrategyType fetchStrategy = attributeDefinition.getFetchStrategy(); if (fetchStrategy != null && fetchStrategy == AttributeFetchStrategyType.EXPLICIT) { explicit.add(attributeDefinition); } else if (hasMinimal && fetchStrategy != AttributeFetchStrategyType.MINIMAL) { explicit.add(attributeDefinition); } } if (!explicit.isEmpty()) { attributesToReturn.setAttributesToReturn(explicit); apply = true; } // Password CredentialsCapabilityType credentialsCapabilityType = ResourceTypeUtil.getEffectiveCapability( resource, CredentialsCapabilityType.class); if (credentialsCapabilityType != null) { if (SelectorOptions.hasToLoadPath(SchemaConstants.PATH_PASSWORD_VALUE, ctx.getGetOperationOptions())) { attributesToReturn.setReturnPasswordExplicit(true); apply = true; } else { if (!CapabilityUtil.isPasswordReturnedByDefault(credentialsCapabilityType)) { // There resource is capable of returning password but it does not // do it by default AttributeFetchStrategyType passwordFetchStrategy = objectClassDefinition .getPasswordFetchStrategy(); if (passwordFetchStrategy == AttributeFetchStrategyType.EXPLICIT) { attributesToReturn.setReturnPasswordExplicit(true); apply = true; } } } } // Activation ActivationCapabilityType activationCapabilityType = ResourceTypeUtil.getEffectiveCapability(resource, ActivationCapabilityType.class); if (activationCapabilityType != null) { if (CapabilityUtil.isCapabilityEnabled(activationCapabilityType.getStatus())) { if (!CapabilityUtil.isActivationStatusReturnedByDefault(activationCapabilityType)) { // There resource is capable of returning enable flag but it does // not do it by default if (SelectorOptions.hasToLoadPath(SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS, ctx.getGetOperationOptions())) { attributesToReturn.setReturnAdministrativeStatusExplicit(true); apply = true; } else { AttributeFetchStrategyType administrativeStatusFetchStrategy = objectClassDefinition .getActivationFetchStrategy(ActivationType.F_ADMINISTRATIVE_STATUS); if (administrativeStatusFetchStrategy == AttributeFetchStrategyType.EXPLICIT) { attributesToReturn.setReturnAdministrativeStatusExplicit(true); apply = true; } } } } if (CapabilityUtil.isCapabilityEnabled(activationCapabilityType.getValidFrom())) { if (!CapabilityUtil.isActivationValidFromReturnedByDefault(activationCapabilityType)) { if (SelectorOptions.hasToLoadPath(SchemaConstants.PATH_ACTIVATION_VALID_FROM, ctx.getGetOperationOptions())) { attributesToReturn.setReturnValidFromExplicit(true); apply = true; } else { AttributeFetchStrategyType administrativeStatusFetchStrategy = objectClassDefinition .getActivationFetchStrategy(ActivationType.F_VALID_FROM); if (administrativeStatusFetchStrategy == AttributeFetchStrategyType.EXPLICIT) { attributesToReturn.setReturnValidFromExplicit(true); apply = true; } } } } if (CapabilityUtil.isCapabilityEnabled(activationCapabilityType.getValidTo())) { if (!CapabilityUtil.isActivationValidToReturnedByDefault(activationCapabilityType)) { if (SelectorOptions.hasToLoadPath(SchemaConstants.PATH_ACTIVATION_VALID_TO, ctx.getGetOperationOptions())) { attributesToReturn.setReturnValidToExplicit(true); apply = true; } else { AttributeFetchStrategyType administrativeStatusFetchStrategy = objectClassDefinition .getActivationFetchStrategy(ActivationType.F_VALID_TO); if (administrativeStatusFetchStrategy == AttributeFetchStrategyType.EXPLICIT) { attributesToReturn.setReturnValidToExplicit(true); apply = true; } } } } if (CapabilityUtil.isCapabilityEnabled(activationCapabilityType.getLockoutStatus())) { if (!CapabilityUtil.isActivationLockoutStatusReturnedByDefault(activationCapabilityType)) { // There resource is capable of returning lockout flag but it does // not do it by default if (SelectorOptions.hasToLoadPath(SchemaConstants.PATH_ACTIVATION_LOCKOUT_STATUS, ctx.getGetOperationOptions())) { attributesToReturn.setReturnAdministrativeStatusExplicit(true); apply = true; } else { AttributeFetchStrategyType statusFetchStrategy = objectClassDefinition .getActivationFetchStrategy(ActivationType.F_LOCKOUT_STATUS); if (statusFetchStrategy == AttributeFetchStrategyType.EXPLICIT) { attributesToReturn.setReturnLockoutStatusExplicit(true); apply = true; } } } } } if (apply) { return attributesToReturn; } else { return null; } } public static <T> PropertyDelta<T> narrowPropertyDelta(PropertyDelta<T> propertyDelta, PrismObject<ShadowType> currentShadow, QName overridingMatchingRuleQName, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException { QName matchingRuleQName = overridingMatchingRuleQName; ItemDefinition propertyDef = propertyDelta.getDefinition(); if (matchingRuleQName == null && propertyDef instanceof RefinedAttributeDefinition) { matchingRuleQName = ((RefinedAttributeDefinition)propertyDef).getMatchingRuleQName(); } MatchingRule<T> matchingRule = null; if (matchingRuleQName != null && propertyDef != null) { matchingRule = matchingRuleRegistry.getMatchingRule(matchingRuleQName, propertyDef.getTypeName()); } LOGGER.trace("Narrowing attr def={}, matchingRule={} ({})", propertyDef, matchingRule, matchingRuleQName); PropertyDelta<T> filteredDelta = propertyDelta.narrow(currentShadow, matchingRule); if (LOGGER.isTraceEnabled() && !filteredDelta.equals(propertyDelta)) { LOGGER.trace("Narrowed delta: {}", filteredDelta.debugDump()); } return filteredDelta; } public static RefinedResourceSchema getRefinedSchema(ResourceType resourceType) throws SchemaException, ConfigurationException { RefinedResourceSchema refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resourceType); if (refinedSchema == null) { throw new ConfigurationException("No schema for "+resourceType); } return refinedSchema; } public static boolean isProtectedShadow(RefinedObjectClassDefinition objectClassDefinition, PrismObject<ShadowType> shadow, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException { boolean isProtected = false; if (objectClassDefinition == null) { isProtected = false; } else { Collection<ResourceObjectPattern> protectedAccountPatterns = objectClassDefinition.getProtectedObjectPatterns(); if (protectedAccountPatterns == null) { isProtected = false; } else { isProtected = ResourceObjectPattern.matches(shadow, protectedAccountPatterns, matchingRuleRegistry); } } LOGGER.trace("isProtectedShadow: {}: {} = {}", new Object[] { objectClassDefinition, shadow, isProtected }); return isProtected; } public static void setProtectedFlag(ProvisioningContext ctx, PrismObject<ShadowType> resourceObject, MatchingRuleRegistry matchingRuleRegistry) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException { if (isProtectedShadow(ctx.getObjectClassDefinition(), resourceObject, matchingRuleRegistry)) { resourceObject.asObjectable().setProtectedObject(true); } } public static RefinedResourceSchema getRefinedSchema(PrismObject<ResourceType> resource) throws SchemaException, ConfigurationException { RefinedResourceSchema refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resource); if (refinedSchema == null) { throw new ConfigurationException("No schema for "+resource); } return refinedSchema; } public static void recordFatalError(Trace logger, OperationResult opResult, String message, Throwable ex) { if (message == null) { message = ex.getMessage(); } logger.error(message, ex); opResult.recordFatalError(message, ex); opResult.cleanupResult(ex); } public static void logWarning(Trace logger, OperationResult opResult, String message, Exception ex) { logger.error(message, ex); opResult.recordWarning(message, ex); } public static boolean shouldStoreAtributeInShadow(RefinedObjectClassDefinition objectClassDefinition, QName attributeName, CachingStategyType cachingStrategy) throws ConfigurationException { if (cachingStrategy == CachingStategyType.NONE) { if (objectClassDefinition.isPrimaryIdentifier(attributeName) || objectClassDefinition.isSecondaryIdentifier(attributeName)) { return true; } for (RefinedAssociationDefinition associationDef: objectClassDefinition.getAssociationDefinitions()) { if (associationDef.getResourceObjectAssociationType().getDirection() == ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) { QName valueAttributeName = associationDef.getResourceObjectAssociationType().getValueAttribute(); if (QNameUtil.match(attributeName, valueAttributeName)) { return true; } } } return false; } else if (cachingStrategy == CachingStategyType.PASSIVE) { RefinedAttributeDefinition<Object> attrDef = objectClassDefinition.findAttributeDefinition(attributeName); return attrDef != null; } else { throw new ConfigurationException("Unknown caching strategy "+cachingStrategy); } } public static boolean shouldStoreActivationItemInShadow(QName elementName, CachingStategyType cachingStrategy) { // MID-2585 if (cachingStrategy == CachingStategyType.PASSIVE) { return true; } else { return QNameUtil.match(elementName, ActivationType.F_ARCHIVE_TIMESTAMP) || QNameUtil.match(elementName, ActivationType.F_DISABLE_TIMESTAMP) || QNameUtil.match(elementName, ActivationType.F_ENABLE_TIMESTAMP) || QNameUtil.match(elementName, ActivationType.F_DISABLE_REASON); } } public static void cleanupShadowActivation(ShadowType repoShadowType) { // cleanup activation - we don't want to store these data in repo shadow (MID-2585) if (repoShadowType.getActivation() != null) { cleanupShadowActivation(repoShadowType.getActivation()); } } public static void cleanupShadowActivation(ActivationType a) { a.setAdministrativeStatus(null); a.setEffectiveStatus(null); a.setValidFrom(null); a.setValidTo(null); a.setValidityStatus(null); a.setLockoutStatus(null); a.setLockoutExpirationTimestamp(null); a.setValidityChangeTimestamp(null); } public static void cleanupShadowPassword(PasswordType p) { p.setValue(null); } public static void addPasswordMetadata(PasswordType p, XMLGregorianCalendar now, PrismObject<UserType> owner) { MetadataType metadata = p.getMetadata(); if (metadata != null) { return; } // Supply some metadata if they are not present. However the // normal thing is that those metadata are provided by model metadata = new MetadataType(); metadata.setCreateTimestamp(now); if (owner != null) { metadata.creatorRef(owner.getOid(), null); } p.setMetadata(metadata); } public static void checkShadowActivationConsistency(PrismObject<ShadowType> shadow) { if (shadow == null) { // just for sure return; } ActivationType activation = shadow.asObjectable().getActivation(); if (activation == null) { return; } FailedOperationTypeType failedOperation = shadow.asObjectable().getFailedOperationType(); if (failedOperation == FailedOperationTypeType.ADD) { return; // in this case it's ok to have activation present } if (activation.getAdministrativeStatus() != null || activation.getEffectiveStatus() != null || activation.getValidFrom() != null || activation.getValidTo() != null || activation.getValidityStatus() != null || activation.getLockoutStatus() != null || activation.getLockoutExpirationTimestamp() != null || activation.getValidityChangeTimestamp() != null) { String m = "Unexpected content in shadow.activation for " + ObjectTypeUtil.toShortString(shadow) + ": " + activation; LOGGER.warn("{}", m); //throw new IllegalStateException(m); // use only for testing } } public static CachingStategyType getCachingStrategy(ProvisioningContext ctx) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { ResourceType resource = ctx.getResource(); CachingPolicyType caching = resource.getCaching(); if (caching == null || caching.getCachingStategy() == null) { ReadCapabilityType readCapabilityType = ResourceTypeUtil.getEffectiveCapability(resource, ReadCapabilityType.class); Boolean cachingOnly = readCapabilityType.isCachingOnly(); if (cachingOnly == Boolean.TRUE) { return CachingStategyType.PASSIVE; } return CachingStategyType.NONE; } return caching.getCachingStategy(); } public static boolean shouldDoRepoSearch(GetOperationOptions rootOptions) { return GetOperationOptions.isNoFetch(rootOptions) || GetOperationOptions.isMaxStaleness(rootOptions); } }