diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/Item.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/Item.java index 733321e80d6..37338eba0bb 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/Item.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/Item.java @@ -611,7 +611,7 @@ protected ItemDelta fixupDelta(ItemDelta delta, Item other, @Override public void accept(Visitor visitor) { visitor.visit(this); - for(PrismValue value: getValues()) { + for (PrismValue value: getValues()) { value.accept(visitor); } } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java index 74aeb8b385c..39f18b9ac9d 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java @@ -640,7 +640,7 @@ public > v if (cval == null) { return; } - cval.removeItem(ItemPath.pathRestStartingWithName(path.rest()), itemType); + cval.removeItem(ItemPath.pathRestStartingWithName(path), itemType); } // Expects that the "self" path segment is NOT included in the basePath diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java index 241bfaad4d1..cba27dab8a3 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java @@ -911,7 +911,7 @@ > void rem Item item = itemsIterator.next(); if (subName.equals(item.getElementName())) { if (!rest.isEmpty() && item instanceof PrismContainer) { - ((PrismContainer)item).removeItem(propPath, itemType); + ((PrismContainer)item).removeItem(rest, itemType); return; } else { if (itemType.isAssignableFrom(item.getClass())) { @@ -951,7 +951,7 @@ public void recompute(PrismContext prismContext) { public void accept(Visitor visitor) { super.accept(visitor); if (items != null) { - for (Item item : getItems()) { + for (Item item : new ArrayList<>(items)) { // to allow modifying item list via the acceptor item.accept(visitor); } } @@ -1766,4 +1766,27 @@ public Collection getAllValues(ItemPath path) { } return rv; } + + public void removeItems(List itemsToRemove) { + for (ItemPath itemToRemove : itemsToRemove) { + Item item = findItem(itemToRemove); // reduce to "removeItem" after fixing that method implementation + if (item != null) { + removeItem(item.getPath(), Item.class); + } + } + } + + public void removeOperationalItems() { + accept(visitable -> { + if (visitable instanceof Item) { + Item item = ((Item) visitable); + if (item.getDefinition() != null && item.getDefinition().isOperational()) { + PrismValue parent = item.getParent(); + if (parent instanceof PrismContainerValue) { // should be the case + ((PrismContainerValue) parent).remove(item); + } + } + } + }); + } } diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestMiscellaneous.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestMiscellaneous.java index 24b7b00e1c1..8b9f36ce7df 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestMiscellaneous.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestMiscellaneous.java @@ -15,24 +15,24 @@ */ package com.evolveum.midpoint.schema; -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; import org.xml.sax.SAXException; +import java.io.File; import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.fail; /** @@ -41,6 +41,9 @@ */ public class TestMiscellaneous { + public static final File TEST_DIR = new File("src/test/resources/misc"); + private static final File FILE_ROLE_REMOVE_ITEMS = new File(TEST_DIR, "role-remove-items.xml"); + @BeforeSuite public void setup() throws SchemaException, SAXException, IOException { PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX); @@ -73,4 +76,39 @@ public void singleValuedItems() throws Exception { } } + @Test + public void removeOperationalItems() throws Exception { + System.out.println("===[ removeOperationalItems ]==="); + PrismObject role = getPrismContext().parseObject(FILE_ROLE_REMOVE_ITEMS); + + AtomicInteger propertyValuesBefore = new AtomicInteger(0); + role.accept(o -> { + if (o instanceof PrismPropertyValue) { + propertyValuesBefore.incrementAndGet(); + System.out.println(((PrismPropertyValue) o).getPath() + ": " + ((PrismPropertyValue) o).getValue()); + } + }); + + System.out.println("Property values before: " + propertyValuesBefore); + + role.getValue().removeOperationalItems(); + System.out.println("After operational items removal:\n" + getPrismContext().xmlSerializer().serialize(role)); + + AtomicInteger propertyValuesAfter = new AtomicInteger(0); + role.accept(o -> { + if (o instanceof PrismPropertyValue) { + propertyValuesAfter.incrementAndGet(); + System.out.println(((PrismPropertyValue) o).getPath() + ": " + ((PrismPropertyValue) o).getValue()); + } + }); + System.out.println("Property values after: " + propertyValuesAfter); + + assertNull("metadata container present", role.findContainer(RoleType.F_METADATA)); + assertNull("effectiveStatus present", role.findProperty(new ItemPath(RoleType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS))); + assertNull("assignment[1]/activation/effectiveStatus present", + role.findProperty(new ItemPath(RoleType.F_ASSIGNMENT, 1L, AssignmentType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS))); + + assertEquals("Wrong property values after", propertyValuesBefore.intValue()-6, propertyValuesAfter.intValue()); + } + } diff --git a/infra/schema/src/test/resources/misc/role-remove-items.xml b/infra/schema/src/test/resources/misc/role-remove-items.xml new file mode 100644 index 00000000000..23e20123455 --- /dev/null +++ b/infra/schema/src/test/resources/misc/role-remove-items.xml @@ -0,0 +1,61 @@ + + + + name-1 + description-1 + active + + hello world + + + enabled + enabled + + + + + enabled + + + + + + enabled + + + + + + enabled + + + + + + enabled + + + false + conservative + diff --git a/model/model-api/pom.xml b/model/model-api/pom.xml index 871b3183b60..343b5fd3a92 100644 --- a/model/model-api/pom.xml +++ b/model/model-api/pom.xml @@ -105,6 +105,10 @@ org.springframework.security spring-security-ldap + + org.springframework + spring-tx + org.springframework spring-beans diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/authentication/MidPointLdapAuthenticationProvider.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/authentication/MidPointLdapAuthenticationProvider.java index e6791671db0..2db0bce532a 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/authentication/MidPointLdapAuthenticationProvider.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/authentication/MidPointLdapAuthenticationProvider.java @@ -24,6 +24,7 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InternalAuthenticationServiceException; @@ -55,6 +56,9 @@ protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationTo // So, be smart here and try to figure out correct error. throw processInternalAuthenticationException(e, e); + } catch (IncorrectResultSizeDataAccessException e) { + LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e); + throw new BadCredentialsException("LdapAuthentication.bad.user", e); } catch (RuntimeException e) { LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e); auditProvider.auditLoginFailure(authentication.getName(), null, ConnectionEnvironment.create(SchemaConstants.CHANNEL_GUI_USER_URI), "bad credentials"); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java index 9c1ff14cb07..644d539c433 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java @@ -155,7 +155,6 @@ public Response generateValue(@PathParam("type") String t } catch (Exception ex) { parentResult.computeStatus(); response = RestServiceUtil.handleException(parentResult, ex); - } finishRequest(task); @@ -937,8 +936,8 @@ public Response executeScript(@Convertor(ExecuteScriptConvertor.class) ExecuteSc response = RestServiceUtil.createResponse(Response.Status.OK, responseData, result); } } catch (Exception ex) { - response = RestServiceUtil.handleException(result, ex); LoggingUtils.logUnexpectedException(LOGGER, "Couldn't execute script.", ex); + response = RestServiceUtil.handleExceptionNoLog(result, ex); } result.computeStatus(); finishRequest(task); @@ -1029,7 +1028,7 @@ public Response getLog(@QueryParam("fromPosition") Long fromPosition, @QueryPara } catch (Exception ex) { LoggingUtils.logUnexpectedException(LOGGER, "Cannot get log file content: fromPosition={}, maxSize={}", ex, fromPosition, maxSize); - response = RestServiceUtil.handleException(result, ex); + response = RestServiceUtil.handleExceptionNoLog(result, ex); } result.computeStatus(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java index adc115be236..a432acab450 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelController.java @@ -1662,6 +1662,18 @@ public CompareResultType compareObject(PrismObject pro return rv; } + private void removeIgnoredItems(PrismObject object, List ignoreItems) { + if (object != null) { + object.getValue().removeItems(ignoreItems); + } + } + + private void removeOperationalItems(PrismObject object) { + if (object != null) { + object.getValue().removeOperationalItems(); + } + } + private PrismObject fetchCurrentObject(Class type, String oid, PolyString name, Collection> readOptions, Task task, OperationResult result) @@ -1701,37 +1713,6 @@ private PrismObject fetchCurrentObject(Class type, } } - private void removeIgnoredItems(PrismObject object, List ignoreItems) { - if (object == null) { - return; - } - for (ItemPath path : ignoreItems) { - Item item = object.findItem(path); // reduce to "removeItem" after fixing that method implementation - if (item != null) { - object.removeItem(item.getPath(), Item.class); - } - } - } - - // TODO write in cleaner way - private void removeOperationalItems(PrismObject object) { - if (object == null) { - return; - } - final List operationalItems = new ArrayList<>(); - object.accept(visitable -> { - if (visitable instanceof Item) { - Item item = ((Item) visitable); - if (item.getDefinition() != null && item.getDefinition().isOperational()) { - operationalItems.add(item.getPath()); - // it would be nice if we could stop visiting children here but that's not possible now - } - } - }); - LOGGER.trace("Operational items: {}", operationalItems); - removeIgnoredItems(object, operationalItems); - } - private Collection> preProcessOptionsSecurity(Collection> options, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { GetOperationOptions rootOptions = SelectorOptions.findRootOptions(options); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java index 62d899a9c4f..58177a51c77 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java @@ -481,6 +481,7 @@ public ShadowType getLinkedShadow(FocusType focus, String resourceOid, boolean r // It is safe to ignore this error in this method. LOGGER.trace("Ignoring shadow " + linkRef.getOid() + " linked in " + focus + " because it no longer exists in repository"); + getCurrentResult().muteLastSubresultError(); continue; } if (shadowType.getResourceRef().getOid().equals(resourceOid)) { @@ -493,6 +494,7 @@ public ShadowType getLinkedShadow(FocusType focus, String resourceOid, boolean r // It is safe to ignore this error in this method. LOGGER.trace("Ignoring shadow " + linkRef.getOid() + " linked in " + focus + " because it no longer exists on resource"); + getCurrentResult().muteLastSubresultError(); continue; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ContextMapKey.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ContextMapKey.java new file mode 100644 index 00000000000..069412bbfcc --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ContextMapKey.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2018 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.model.impl.integrity; + +import javax.xml.namespace.QName; +import java.io.Serializable; +import java.util.Objects; + +/** + * @author mederly + */ + +class ContextMapKey implements Serializable { + + final String resourceOid; + final QName objectClassName; + + ContextMapKey(String resourceOid, QName objectClassName) { + this.resourceOid = resourceOid; + this.objectClassName = objectClassName; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof ContextMapKey)) + return false; + ContextMapKey that = (ContextMapKey) o; + return Objects.equals(resourceOid, that.resourceOid) && + Objects.equals(objectClassName, that.objectClassName); + } + + @Override + public int hashCode() { + return Objects.hash(resourceOid, objectClassName); + } + + @Override + public String toString() { + return "ContextMapKey{" + + "resourceOid='" + resourceOid + '\'' + + ", objectClassName=" + objectClassName + + '}'; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ObjectTypeContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ObjectTypeContext.java index 8a64564b73c..00182e81dd5 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ObjectTypeContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ObjectTypeContext.java @@ -26,7 +26,7 @@ import java.util.Set; /** - * Checker context related to one object type (resource + kind). + * Checker context related to one object type (resource + object class). * * @author Pavol Mederly */ diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowIntegrityCheckResultHandler.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowIntegrityCheckResultHandler.java index 2b43d2c2554..3f92fe678e3 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowIntegrityCheckResultHandler.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowIntegrityCheckResultHandler.java @@ -48,8 +48,6 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; import javax.xml.namespace.QName; import java.util.*; @@ -100,9 +98,7 @@ public class ShadowIntegrityCheckResultHandler extends AbstractSearchIterativeRe public static final List KNOWN_KEYS = Arrays.asList(INTENTS, UNIQUENESS, NORMALIZATION, OWNERS, FETCH, EXTRA_DATA, RESOURCE_REF); - // resource oid + kind -> ROCD - // we silently assume that all intents for a given kind share a common attribute definition - private Map, ObjectTypeContext> contextMap = new HashMap<>(); + private Map contextMap = new HashMap<>(); private Map> resources = new HashMap<>(); @@ -379,7 +375,13 @@ private void checkShadow(ShadowCheckResult checkResult, PrismObject doFixIntent(checkResult, fetchedShadow, shadow, resource, workerTask, result); } - Pair key = new ImmutablePair<>(resourceOid, kind); + QName objectClassName = shadowType.getObjectClass(); + if (objectClassName == null) { + checkResult.recordError(ShadowStatistics.NO_OBJECT_CLASS_SPECIFIED, new SchemaException("No object class specified")); + return; + } + + ContextMapKey key = new ContextMapKey(resourceOid, objectClassName); ObjectTypeContext context = contextMap.get(key); if (context == null) { context = new ObjectTypeContext(); @@ -647,9 +649,9 @@ private String reportOrFixUniqueness(Task task, OperationResult result) { StringBuilder details = new StringBuilder(); StringBuilder stat = new StringBuilder(); - for (Map.Entry, ObjectTypeContext> entry : contextMap.entrySet()) { - String resourceOid = entry.getKey().getLeft(); - ShadowKindType kind = entry.getKey().getRight(); + for (Map.Entry entry : contextMap.entrySet()) { + String resourceOid = entry.getKey().resourceOid; + QName objectClassName = entry.getKey().objectClassName; ObjectTypeContext ctx = entry.getValue(); PrismObject resource = resources.get(resourceOid); if (resource == null) { @@ -666,7 +668,7 @@ private String reportOrFixUniqueness(Task task, OperationResult result) { } if (first) { details.append("Duplicates for ").append(ObjectTypeUtil.toShortString(resource)); - details.append(", kind = ").append(kind); + details.append(", object class = ").append(objectClassName); details.append(", identifier = ").append(identifier).append(":\n"); first = false; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowStatistics.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowStatistics.java index 13c5c05d5b5..59bd2e697a9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowStatistics.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/integrity/ShadowStatistics.java @@ -35,6 +35,7 @@ public class ShadowStatistics { public static final String NO_RESOURCE_OID = "No resource ref or OID"; public static final String NO_RESOURCE = "No resource"; public static final String CANNOT_GET_RESOURCE = "Cannot get resource object"; + public static final String NO_OBJECT_CLASS_SPECIFIED = "No object class specified"; public static final String NO_KIND_SPECIFIED = "No kind specified"; public static final String NO_INTENT_SPECIFIED = "No intent specified"; public static final String NO_RESOURCE_REFINED_SCHEMA = "No resource refined schema"; @@ -59,6 +60,7 @@ public class ShadowStatistics { NO_RESOURCE_OID, NO_RESOURCE, CANNOT_GET_RESOURCE, + NO_OBJECT_CLASS_SPECIFIED, NO_KIND_SPECIFIED, NO_INTENT_SPECIFIED, NO_RESOURCE_REFINED_SCHEMA, diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java index 079457a6625..9d9ed3cd27c 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java @@ -85,6 +85,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import org.springframework.beans.factory.annotation.Value; import java.util.function.Predicate; /** @@ -92,8 +95,8 @@ * @author semancik */ @Service(value = "userDetailsService") -public class UserProfileServiceImpl implements UserProfileService, UserDetailsService, UserDetailsContextMapper, MessageSourceAware { - +public class UserProfileServiceImpl implements UserProfileService, UserDetailsService, UserDetailsContextMapper, MessageSourceAware { + private static final Trace LOGGER = TraceManager.getTrace(UserProfileServiceImpl.class); @Autowired @@ -111,6 +114,9 @@ public class UserProfileServiceImpl implements UserProfileService, UserDetailsSe @Autowired private PrismContext prismContext; @Autowired private TaskManager taskManager; + //optional application.yml property for LDAP authentication, marks LDAP attribute name that correlates with midPoint UserType name + @Value("${auth.ldap.search.naming-attr:#{null}}") private String ldapNamingAttr; + private MessageSourceAccessor messages; @Override @@ -376,11 +382,17 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx @Override public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection authorities) { - try { - return getPrincipal(username); + + String userNameEffective = username; + try { + if (ctx != null && ldapNamingAttr != null) { + userNameEffective = resolveLdapName(ctx, username); + } + return getPrincipal(userNameEffective); + } catch (ObjectNotFoundException e) { throw new UsernameNotFoundException("UserProfileServiceImpl.unknownUser", e); - } catch (SchemaException e) { + } catch (SchemaException | NamingException e) { throw new SystemException(e.getMessage(), e); } } @@ -391,5 +403,20 @@ public void mapUserToContext(UserDetails user, DirContextAdapter ctx) { } - + private String resolveLdapName(DirContextOperations ctx, String username) throws NamingException, ObjectNotFoundException { + Attribute ldapResponse = ctx.getAttributes().get(ldapNamingAttr); + if (ldapResponse != null) { + if (ldapResponse.size() == 1) { + Object namingAttrValue = ldapResponse.get(0); + + if (namingAttrValue != null) { + return namingAttrValue.toString().toLowerCase(); + } + } + else { + throw new ObjectNotFoundException("Bad response"); // naming attribute contains multiple values + } + } + return username; // fallback to typed-in username in case ldap value is missing + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/RestServiceUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/RestServiceUtil.java index 117687b9ab3..36c20cca026 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/RestServiceUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/util/RestServiceUtil.java @@ -25,7 +25,9 @@ import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import org.apache.commons.codec.binary.Base64; +import com.evolveum.midpoint.util.logging.LoggingUtils; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import org.apache.commons.lang.StringUtils; import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.jaxrs.ext.MessageContext; @@ -35,7 +37,6 @@ import com.evolveum.midpoint.model.impl.security.SecurityHelper; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.security.api.ConnectionEnvironment; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.AuthorizationException; @@ -57,12 +58,20 @@ */ public class RestServiceUtil { + private static final Trace LOGGER = TraceManager.getTrace(RestServiceUtil.class); + public static final String MESSAGE_PROPERTY_TASK_NAME = "task"; private static final String QUERY_PARAMETER_OPTIONS = "options"; public static final String OPERATION_RESULT_STATUS = "OperationResultStatus"; public static final String OPERATION_RESULT_MESSAGE = "OperationResultMessage"; public static Response handleException(OperationResult result, Exception ex) { + LoggingUtils.logUnexpectedException(LOGGER, "Got exception while servicing REST request: {}", ex, + result != null ? result.getOperation() : "(null)"); + return handleExceptionNoLog(result, ex); + } + + public static Response handleExceptionNoLog(OperationResult result, Exception ex) { return createErrorResponseBuilder(result, ex).build(); } diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/misc/ShadowIntegrityCheckerTest.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/misc/ShadowIntegrityCheckerTest.java new file mode 100644 index 00000000000..c4a784fcec8 --- /dev/null +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/misc/ShadowIntegrityCheckerTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2018 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.model.impl.misc; + +import com.evolveum.midpoint.model.impl.AbstractInternalModelIntegrationTest; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.schema.SearchResultList; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.util.MidPointTestConstants; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.List; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; + +/** + * @author mederly + * + */ +@ContextConfiguration(locations = {"classpath:ctx-model-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class ShadowIntegrityCheckerTest extends AbstractInternalModelIntegrationTest { + + protected static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "misc"); + + private static final File TASK_SHADOW_INTEGRITY_CHECK_FILE = new File(TEST_DIR, "task-shadow-integrity-check.xml"); + private static final String TASK_SHADOW_INTEGRITY_CHECK_OID = "b5a8b51d-d834-4803-a7d0-c81bcc58113e"; + + private static final File SHADOW_1_FILE = new File(TEST_DIR, "shadow-1.xml"); + private static final File SHADOW_2_FILE = new File(TEST_DIR, "shadow-2.xml"); + private static final File SHADOW_2_DUPLICATE_FILE = new File(TEST_DIR, "shadow-2-duplicate.xml"); + + private static final File RESOURCE_DUMMY_FOR_CHECKER_FILE = new File(TEST_DIR, "resource-dummy-for-checker.xml"); + private static final String RESOURCE_DUMMY_FOR_CHECKER_OID = "8fdb9db5-429a-4bcc-94f4-043dbd7f2eb2"; + private static final String DUMMY_FOR_CHECKER = "for-checker"; + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + initDummyResourcePirate(DUMMY_FOR_CHECKER, RESOURCE_DUMMY_FOR_CHECKER_FILE, RESOURCE_DUMMY_FOR_CHECKER_OID, initTask, initResult); + + List> shadows = repositoryService + .searchObjects(ShadowType.class, null, null, initResult); + for (PrismObject shadow : shadows) { + repositoryService.deleteObject(ShadowType.class, shadow.getOid(), initResult); + } + repoAddObjectFromFile(SHADOW_1_FILE, initResult); + repoAddObjectFromFile(SHADOW_2_FILE, initResult); + repoAddObjectFromFile(SHADOW_2_DUPLICATE_FILE, initResult); + } + + @Test + public void test100FixDuplicatesWithDifferentObjectClasses() throws Exception { + final String TEST_NAME = "test100FixDuplicatesWithDifferentObjectClasses"; + TestUtil.displayTestTitle(this, TEST_NAME); + + login(userAdministrator); + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // GIVEN + SearchResultList> shadowsBefore = repositoryService + .searchObjects(ShadowType.class, null, null, result); + display("shadows before", shadowsBefore); + + assertEquals("Wrong # of shadows before", 3, shadowsBefore.size()); + + repoAddObjectFromFile(TASK_SHADOW_INTEGRITY_CHECK_FILE, result); + + // WHEN + displayWhen(TEST_NAME); + waitForTaskCloseOrSuspend(TASK_SHADOW_INTEGRITY_CHECK_OID); + + // THEN + displayThen(TEST_NAME); + PrismObject taskAfter = getTask(TASK_SHADOW_INTEGRITY_CHECK_OID); + display("task after", taskAfter); + + SearchResultList> shadowsAfter = repositoryService + .searchObjects(ShadowType.class, null, null, result); + display("shadows after", shadowsAfter); + + assertEquals("Wrong # of shadows after", 2, shadowsAfter.size()); + PrismObject intent1 = shadowsAfter.stream() + .filter(o -> "intent1".equals(o.asObjectable().getIntent())).findFirst().orElse(null); + assertNotNull("intent1 shadow was removed", intent1); + } + + +} diff --git a/model/model-impl/src/test/resources/misc/resource-dummy-for-checker.xml b/model/model-impl/src/test/resources/misc/resource-dummy-for-checker.xml new file mode 100644 index 00000000000..869919b51d3 --- /dev/null +++ b/model/model-impl/src/test/resources/misc/resource-dummy-for-checker.xml @@ -0,0 +1,155 @@ + + + + + + Dummy Resource for checker + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + for-checker + + + + + + + + + + + + + + icfs:uid + icfs:name + icfs:name + icfs:name + __ACCOUNT__ + + + + + + + + + ICF UID + read + + + + + + + ICF NAME + + + + + + + + + + icfs:uid + icfs:name + icfs:name + icfs:name + __ACCOUNT__ + + + + + + + + + ICF UID + read + + + + + + + ICF NAME + + + + + + + + + + + + account + intent1 + false + ri:account1 + + icfs:name + + + icfs:uid + + + + account + intent2 + false + ri:account2 + + icfs:name + + + icfs:uid + + + + diff --git a/model/model-impl/src/test/resources/misc/shadow-1.xml b/model/model-impl/src/test/resources/misc/shadow-1.xml new file mode 100644 index 00000000000..d96d06067e7 --- /dev/null +++ b/model/model-impl/src/test/resources/misc/shadow-1.xml @@ -0,0 +1,29 @@ + + + + identity-A + + ri:account1 + account + intent1 + + identity-A + uid-1 + + \ No newline at end of file diff --git a/model/model-impl/src/test/resources/misc/shadow-2-duplicate.xml b/model/model-impl/src/test/resources/misc/shadow-2-duplicate.xml new file mode 100644 index 00000000000..8eac597cff6 --- /dev/null +++ b/model/model-impl/src/test/resources/misc/shadow-2-duplicate.xml @@ -0,0 +1,29 @@ + + + + identity-A + + ri:account2 + account + intent2 + + identity-A + uid-2x + + \ No newline at end of file diff --git a/model/model-impl/src/test/resources/misc/shadow-2.xml b/model/model-impl/src/test/resources/misc/shadow-2.xml new file mode 100644 index 00000000000..6e34bffb670 --- /dev/null +++ b/model/model-impl/src/test/resources/misc/shadow-2.xml @@ -0,0 +1,29 @@ + + + + identity-A + + ri:account2 + account + intent2 + + identity-A + uid-2 + + \ No newline at end of file diff --git a/model/model-impl/src/test/resources/misc/task-shadow-integrity-check.xml b/model/model-impl/src/test/resources/misc/task-shadow-integrity-check.xml new file mode 100644 index 00000000000..a1e8a6c3f19 --- /dev/null +++ b/model/model-impl/src/test/resources/misc/task-shadow-integrity-check.xml @@ -0,0 +1,27 @@ + + + + Shadow Integrity Check + + uniqueness + uniqueness + + + runnable + http://midpoint.evolveum.com/xml/ns/public/model/shadow-integrity-check/handler-3 + single + \ No newline at end of file diff --git a/model/model-impl/testng-unit.xml b/model/model-impl/testng-unit.xml index 90f316133bd..a16d21d308b 100644 --- a/model/model-impl/testng-unit.xml +++ b/model/model-impl/testng-unit.xml @@ -83,6 +83,8 @@ + + diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java index dec58f899f1..54aa9e7796d 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMisc.java @@ -37,12 +37,14 @@ import com.evolveum.icf.dummy.resource.DummyAccount; import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.RepositoryDiag; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.DummyResourceContoller; import com.evolveum.midpoint.test.util.TestUtil; @@ -63,6 +65,9 @@ public class TestMisc extends AbstractInitializedModelIntegrationTest { protected static final File ROLE_IMPORT_FILTERS_FILE = new File(TEST_DIR, "role-import-filters.xml"); protected static final String ROLE_IMPORT_FILTERS_OID = "aad19b9a-d511-11e7-8bf7-cfecde275e59"; + protected static final File ROLE_SHIP_FILE = new File(TEST_DIR, "role-ship.xml"); + protected static final String ROLE_SHIP_OID = "bbd19b9a-d511-11e7-8bf7-cfecde275e59"; + protected static final File RESOURCE_SCRIPTY_FILE = new File(TEST_DIR, "resource-dummy-scripty.xml"); protected static final String RESOURCE_SCRIPTY_OID = "399f5308-0447-11e8-91e9-a7f9c4100ffb"; protected static final String RESOURCE_DUMMY_SCRIPTY_NAME = "scripty"; @@ -82,6 +87,8 @@ public void initSystem(Task initTask, OperationResult initResult) initDummyResourcePirate(RESOURCE_DUMMY_SCRIPTY_NAME, RESOURCE_SCRIPTY_FILE, RESOURCE_SCRIPTY_OID, initTask, initResult); + + importObjectFromFile(ROLE_SHIP_FILE); } @Test @@ -327,4 +334,95 @@ public void test500AddHocProvisioningScriptAssignJackResourceScripty() throws Ex "Mr. POLY JACK SPARROW"); } + + /** + * MID-4504 + * midpoint.getLinkedShadow fails recomputing without throwing exception during shadow delete + * + * the ship attribute in the role "Ship" has mapping with calling midpoint.getLinkedShadow() on the reosurce which doesn't exist + */ + @Test + public void test600jackAssignRoleShip() throws Exception { + final String TEST_NAME = "test600jackAssignRoleShip"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + + // WHEN + displayWhen(TEST_NAME); + assignRole(USER_JACK_OID, ROLE_SHIP_OID); + + //THEN + displayThen(TEST_NAME); + PrismObject userAfter = getUser(USER_JACK_OID); + display("User before", userAfter); + assertAssignments(userAfter, 2); + assertLinks(userAfter, 1); + + PrismReference linkRef = userAfter.findReference(UserType.F_LINK_REF); + assertTrue(!linkRef.isEmpty()); + +// PrismObject shadowModel = getShadowModel(linkRef.getOid()); + + assertDummyAccountAttribute(RESOURCE_DUMMY_SCRIPTY_NAME, USER_JACK_USERNAME, "ship", "ship"); + + } + + @Test + public void test601jackUnassignResourceAccount() throws Exception { + final String TEST_NAME = "test601jackUnassignResourceAccount"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertAssignments(userBefore, 2); + + // WHEN + displayWhen(TEST_NAME); + unassignAccount(USER_JACK_OID, RESOURCE_SCRIPTY_OID, null); + + //THEN + displayThen(TEST_NAME); + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertAssignments(userAfter, 1); + assertLinks(userAfter, 1); + } + + + /** + * MID-4504 + * midpoint.getLinkedShadow fails recomputing without throwing exception during shadow delete + * + * first assign role ship, the ship attribute in the role has mapping with calling midpoint.getLinkedShadow() + */ + @Test + public void test602jackUnssigndRoleShip() throws Exception { + final String TEST_NAME = "test602jackUnssigndRoleShip"; + displayTestTitle(TEST_NAME); + + // GIVEN + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + + // WHEN + displayWhen(TEST_NAME); + unassignRole(USER_JACK_OID, ROLE_SHIP_OID); + + //THEN + displayThen(TEST_NAME); + PrismObject userAfter = getUser(USER_JACK_OID); + display("User before", userAfter); + assertAssignments(userAfter, 0); + assertLinks(userAfter, 0); + + } + } diff --git a/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml b/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml index 3c91bc7d07b..bb1ceeb8eb6 100644 --- a/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml +++ b/model/model-intest/src/test/resources/misc/resource-dummy-scripty.xml @@ -21,7 +21,7 @@ xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3" xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3" xmlns:icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3" - xmlns:ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004" + xmlns:ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3" xmlns:mr="http://prism.evolveum.com/xml/ns/public/matching-rule-3" xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3" xmlns:piracy="http://midpoint.evolveum.com/xml/ns/samples/piracy" @@ -62,8 +62,6 @@ - http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004 - account diff --git a/model/model-intest/src/test/resources/misc/role-ship.xml b/model/model-intest/src/test/resources/misc/role-ship.xml new file mode 100644 index 00000000000..3e591c6fda8 --- /dev/null +++ b/model/model-intest/src/test/resources/misc/role-ship.xml @@ -0,0 +1,34 @@ + + + Space Ship + MID-4504 + + + + account + + ri:ship + + strong + + + + + + + + \ No newline at end of file diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/ActivitiInterface.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/ActivitiInterface.java index c606ff872f7..a145b80cf02 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/ActivitiInterface.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/ActivitiInterface.java @@ -103,7 +103,7 @@ public void startActivitiProcessInstance(StartProcessCommand spic, Task task, Op processInterfaceFinder); event.setRunning(!pi.isEnded()); LOGGER.trace("Event to be sent to IDM: {}", event); - wfTaskController.onProcessEvent(event, task, result); + wfTaskController.onProcessEvent(event, true, task, result); } } @@ -150,7 +150,7 @@ public void queryActivitiProcessInstance(QueryProcessCommand qpc, Task task, Ope LOGGER.trace("Running process instance = {}, isRunning: {}", pi, qpr.isRunning()); LOGGER.trace("Response to be sent to midPoint: {}", qpr); - wfTaskController.onProcessEvent(qpr, task, result); + wfTaskController.onProcessEvent(qpr, false, task, result); } public void notifyMidpointAboutProcessFinishedEvent(DelegateExecution execution) { @@ -176,7 +176,7 @@ private void notifyMidpointAboutProcessEvent(ProcessEvent event) { return; } try { - wfTaskController.onProcessEvent(event, task, result); + wfTaskController.onProcessEvent(event, false, task, result); } catch (SchemaException|ObjectNotFoundException|ObjectAlreadyExistsException|RuntimeException e) { throw new SystemException("Couldn't process a process-related event: " + e.getMessage(), e); } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java index 72f5fca22aa..b706658913b 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java @@ -49,7 +49,6 @@ import com.evolveum.midpoint.wf.impl.processors.primary.PcpWfTask; import com.evolveum.midpoint.wf.impl.processors.primary.PrimaryChangeProcessor; import com.evolveum.midpoint.wf.impl.util.MiscDataUtil; -import com.evolveum.midpoint.wf.util.ApprovalUtils; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; import org.apache.commons.lang.BooleanUtils; @@ -230,7 +229,12 @@ private void startWorkflowProcessInstance(WfTask wfTask, WfTaskCreationInstructi LOGGER.trace("startWorkflowProcessInstance finished"); } - public void onProcessEvent(ProcessEvent event, Task task, OperationResult result) + // skipProcessEndNotification is a bit of hack: It is to avoid sending process end notification twice if the process ends + // in the same thread in which it was started (MID-4850). It could be probably solved in a more brave way e.g. by removing + // the whole onProcessEvent call in startActivitiProcessInstance but that could have other consequences. + // + // We get rid of these hacks when we replace Activiti with our own implementation (4.0 or 4.1). + public void onProcessEvent(ProcessEvent event, boolean skipProcessEndNotification, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { WfTask wfTask = recreateWfTask(task); @@ -249,7 +253,7 @@ public void onProcessEvent(ProcessEvent event, Task task, OperationResult result wfTask.commitChanges(result); - if (event instanceof ProcessFinishedEvent || !event.isRunning()) { + if (!skipProcessEndNotification && (event instanceof ProcessFinishedEvent || !event.isRunning())) { onProcessFinishedEvent(event, wfTask, result); } } diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/legacy/TestUserChangeApprovalLegacy.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/legacy/TestUserChangeApprovalLegacy.java index 6a4c2e5b44f..e3c37f5b615 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/legacy/TestUserChangeApprovalLegacy.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/legacy/TestUserChangeApprovalLegacy.java @@ -161,6 +161,8 @@ boolean decideOnApproval(String executionId) throws Exception { return decideOnRoleApproval(executionId); } }); + displayAllNotifications(); + checkDummyTransportMessages("simpleWorkflowNotifier-Processes", 2); // start + end } protected void assertWfContextAfterClockworkRun(Task rootTask, List subtasks, OperationResult result, String... processNames) throws Exception { @@ -774,6 +776,8 @@ boolean decideOnApproval(String executionId) throws Exception { } }); + displayAllNotifications(); + checkDummyTransportMessages("simpleWorkflowNotifier-Processes", 2); // start + end } @Test(enabled = true) @@ -838,6 +842,8 @@ boolean decideOnApproval(String executionId) throws Exception { } }); + displayAllNotifications(); + checkDummyTransportMessages("simpleWorkflowNotifier-Processes", 2); // start + end } @Test diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java index 0217211b759..4043954f587 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java @@ -19,6 +19,8 @@ import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; import org.springframework.stereotype.Component; import com.evolveum.midpoint.prism.PrismContext; @@ -36,6 +38,8 @@ @Component public class ResourceCache { + private static final Trace LOGGER = TraceManager.getTrace(ResourceCache.class); + private Map> cache; ResourceCache() { @@ -94,7 +98,14 @@ public synchronized PrismObject get(String oid, String version, Ge } if (GetOperationOptions.isReadOnly(options)) { - cachedResource.checkImmutability(); + try { // MID-4574 + cachedResource.checkImmutability(); + } catch (IllegalStateException ex) { + LOGGER.error("Failed immutability test", ex); + cache.remove(oid); + + return null; + } return cachedResource; } else { return cachedResource.clone();