Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 8FE51200AF7 for ; Tue, 14 Jun 2016 17:58:20 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 8E886160A47; Tue, 14 Jun 2016 15:58:20 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id DC170160A6B for ; Tue, 14 Jun 2016 17:58:18 +0200 (CEST) Received: (qmail 20633 invoked by uid 500); 14 Jun 2016 15:58:18 -0000 Mailing-List: contact commits-help@syncope.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@syncope.apache.org Delivered-To: mailing list commits@syncope.apache.org Received: (qmail 20396 invoked by uid 99); 14 Jun 2016 15:58:17 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 14 Jun 2016 15:58:17 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id D4678E07FE; Tue, 14 Jun 2016 15:58:17 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ilgrosso@apache.org To: commits@syncope.apache.org Date: Tue, 14 Jun 2016 15:58:30 -0000 Message-Id: <44958547e6d746d68b31b3cc1b38349e@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [14/17] syncope git commit: [SYNCOPE-862] Features complete archived-at: Tue, 14 Jun 2016 15:58:20 -0000 http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java index a2c9271..4c44b7f 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java @@ -24,7 +24,6 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.policy.PullPolicySpec; -import org.apache.syncope.core.provisioning.java.MappingManagerImpl; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; import org.apache.syncope.core.persistence.api.dao.AnyDAO; @@ -47,8 +46,8 @@ import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.Connector; -import org.apache.syncope.core.provisioning.api.IntAttrNameParser; -import org.apache.syncope.core.provisioning.api.IntAttrNameParser.IntAttrName; +import org.apache.syncope.core.provisioning.java.IntAttrNameParser; +import org.apache.syncope.core.provisioning.api.IntAttrName; import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeUtil; @@ -63,6 +62,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule; +import org.apache.syncope.core.provisioning.java.utils.MappingUtils; @Transactional(readOnly = true) @Component @@ -103,6 +103,9 @@ public class PullUtils { @Autowired private AnyUtilsFactory anyUtilsFactory; + @Autowired + private IntAttrNameParser intAttrNameParser; + public String findMatchingAnyKey( final AnyType anyType, final String name, @@ -127,7 +130,7 @@ public class PullUtils { public boolean handle(final ConnectorObject obj) { return found.add(obj); } - }, MappingManagerImpl.buildOperationOptions(MappingManagerImpl.getPullMappingItems(provision).iterator())); + }, MappingUtils.buildOperationOptions(MappingUtils.getPullMappingItems(provision).iterator())); if (found.isEmpty()) { LOG.debug("No {} found on {} with __NAME__ {}", provision.getObjectClass(), resource, name); @@ -170,10 +173,10 @@ public class PullUtils { List result = new ArrayList<>(); - MappingItem connObjectKeyItem = MappingManagerImpl.getConnObjectKeyItem(provision); + MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision); String transfUid = uid; - for (MappingItemTransformer transformer : MappingManagerImpl.getMappingItemTransformers(connObjectKeyItem)) { + for (MappingItemTransformer transformer : MappingUtils.getMappingItemTransformers(connObjectKeyItem)) { List output = transformer.beforePull( connObjectKeyItem, null, @@ -183,9 +186,8 @@ public class PullUtils { } } - IntAttrName intAttrName = IntAttrNameParser.parse( + IntAttrName intAttrName = intAttrNameParser.parse( connObjectKeyItem.getIntAttrName(), - anyUtilsFactory, provision.getAnyType().getKind()); if (intAttrName.getField() != null) { @@ -214,6 +216,8 @@ public class PullUtils { result.add(anyObject.getKey()); } break; + + default: } } else if (intAttrName.getSchemaType() != null) { switch (intAttrName.getSchemaType()) { @@ -246,6 +250,8 @@ public class PullUtils { result.add(any.getKey()); } break; + + default: } } http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java index af2631c..c0ed5fc 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.provisioning.java.utils; -import org.apache.syncope.core.provisioning.java.MappingManagerImpl; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -222,7 +221,7 @@ public class ConnObjectUtils { // 1. fill with data from connector object anyTO.setRealm(pullTask.getDestinatioRealm().getFullPath()); - for (MappingItem item : MappingManagerImpl.getPullMappingItems(provision)) { + for (MappingItem item : MappingUtils.getPullMappingItems(provision)) { mappingManager.setIntValues(item, obj.getAttributeByName(item.getExtAttrName()), anyTO, anyUtils); } http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java new file mode 100644 index 0000000..63ceb98 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java.utils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import org.apache.commons.jexl3.JexlContext; +import org.apache.commons.jexl3.MapContext; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.resource.Mapping; +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer; +import org.apache.syncope.core.provisioning.java.data.JEXLMappingItemTransformer; +import org.apache.syncope.core.provisioning.java.jexl.JexlUtils; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.identityconnectors.framework.common.objects.Name; +import org.identityconnectors.framework.common.objects.OperationOptions; +import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; +import org.identityconnectors.framework.common.objects.OperationalAttributes; +import org.identityconnectors.framework.common.objects.Uid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; + +public final class MappingUtils { + + private static final Logger LOG = LoggerFactory.getLogger(MappingUtils.class); + + public static MappingItem getConnObjectKeyItem(final Provision provision) { + Mapping mapping = null; + if (provision != null) { + mapping = provision.getMapping(); + } + + return mapping == null + ? null + : mapping.getConnObjectKeyItem(); + } + + private static List getMappingItems(final Provision provision, final MappingPurpose purpose) { + List items = Collections.emptyList(); + if (provision != null) { + items = provision.getMapping().getItems(); + } + + List result = new ArrayList<>(); + + switch (purpose) { + case PULL: + for (MappingItem item : items) { + if (MappingPurpose.PROPAGATION != item.getPurpose() + && MappingPurpose.NONE != item.getPurpose()) { + + result.add(item); + } + } + break; + + case PROPAGATION: + for (MappingItem item : items) { + if (MappingPurpose.PULL != item.getPurpose() + && MappingPurpose.NONE != item.getPurpose()) { + + result.add(item); + } + } + break; + + case BOTH: + for (MappingItem item : items) { + if (MappingPurpose.NONE != item.getPurpose()) { + result.add(item); + } + } + break; + + case NONE: + for (MappingItem item : items) { + if (MappingPurpose.NONE == item.getPurpose()) { + result.add(item); + } + } + break; + + default: + } + + return result; + } + + public static List getPropagationMappingItems(final Provision provision) { + return getMappingItems(provision, MappingPurpose.PROPAGATION); + } + + public static List getPullMappingItems(final Provision provision) { + return getMappingItems(provision, MappingPurpose.PULL); + } + + /** + * Build __NAME__ for propagation. First look if there ia a defined connObjectLink for the given resource (and in + * this case evaluate as JEXL); otherwise, take given connObjectKey. + * + * @param any given any object + * @param provision external resource + * @param connObjectKey connector object key + * @return the value to be propagated as __NAME__ + */ + public static Name evaluateNAME(final Any any, final Provision provision, final String connObjectKey) { + if (StringUtils.isBlank(connObjectKey)) { + // LOG error but avoid to throw exception: leave it to the external resource + LOG.error("Missing ConnObjectKey for '{}': ", provision.getResource()); + } + + // Evaluate connObjectKey expression + String connObjectLink = provision == null || provision.getMapping() == null + ? null + : provision.getMapping().getConnObjectLink(); + String evalConnObjectLink = null; + if (StringUtils.isNotBlank(connObjectLink)) { + JexlContext jexlContext = new MapContext(); + JexlUtils.addFieldsToContext(any, jexlContext); + JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext); + JexlUtils.addDerAttrsToContext(any, jexlContext); + evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext); + } + + // If connObjectLink evaluates to an empty string, just use the provided connObjectKey as Name(), + // otherwise evaluated connObjectLink expression is taken as Name(). + Name name; + if (StringUtils.isBlank(evalConnObjectLink)) { + // add connObjectKey as __NAME__ attribute ... + LOG.debug("Add connObjectKey [{}] as __NAME__", connObjectKey); + name = new Name(connObjectKey); + } else { + LOG.debug("Add connObjectLink [{}] as __NAME__", evalConnObjectLink); + name = new Name(evalConnObjectLink); + + // connObjectKey not propagated: it will be used to set the value for __UID__ attribute + LOG.debug("connObjectKey will be used just as __UID__ attribute"); + } + + return name; + } + + public static List getMappingItemTransformers(final MappingItem mappingItem) { + List result = new ArrayList<>(); + + // First consider the JEXL transformation expressions + JEXLMappingItemTransformer jexlTransformer = null; + if (StringUtils.isNotBlank(mappingItem.getPropagationJEXLTransformer()) + || StringUtils.isNotBlank(mappingItem.getPullJEXLTransformer())) { + + try { + jexlTransformer = (JEXLMappingItemTransformer) ApplicationContextProvider.getBeanFactory(). + createBean(JEXLMappingItemTransformer.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + + jexlTransformer.setPropagationJEXL(mappingItem.getPropagationJEXLTransformer()); + jexlTransformer.setPullJEXL(mappingItem.getPullJEXLTransformer()); + } catch (Exception e) { + LOG.error("Could not instantiate {}, ignoring...", JEXLMappingItemTransformer.class.getName(), e); + } + } + if (jexlTransformer != null) { + result.add(jexlTransformer); + } + + // Then other custom tranaformers + for (String className : mappingItem.getMappingItemTransformerClassNames()) { + try { + Class transformerClass = ClassUtils.getClass(className); + + result.add((MappingItemTransformer) ApplicationContextProvider.getBeanFactory(). + createBean(transformerClass, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false)); + } catch (Exception e) { + LOG.error("Could not instantiate {}, ignoring...", className, e); + } + } + + return result; + } + + /** + * Build options for requesting all mapped connector attributes. + * + * @param mapItems mapping items + * @return options for requesting all mapped connector attributes + * @see OperationOptions + */ + public static OperationOptions buildOperationOptions(final Iterator mapItems) { + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + + Set attrsToGet = new HashSet<>(); + attrsToGet.add(Name.NAME); + attrsToGet.add(Uid.NAME); + attrsToGet.add(OperationalAttributes.ENABLE_NAME); + + while (mapItems.hasNext()) { + MappingItem mapItem = mapItems.next(); + if (mapItem.getPurpose() != MappingPurpose.NONE) { + attrsToGet.add(mapItem.getExtAttrName()); + } + } + + builder.setAttributesToGet(attrsToGet); + // ------------------------------------- + + return builder.build(); + } + + /** + * Private default constructor, for static-only classes. + */ + private MappingUtils() { + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/core/provisioning-java/src/main/resources/provisioningContext.xml ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/resources/provisioningContext.xml b/core/provisioning-java/src/main/resources/provisioningContext.xml index 068d1e7..37c8181 100644 --- a/core/provisioning-java/src/main/resources/provisioningContext.xml +++ b/core/provisioning-java/src/main/resources/provisioningContext.xml @@ -122,4 +122,5 @@ under the License. + http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java new file mode 100644 index 0000000..c603f8a --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.SchemaType; +import org.apache.syncope.core.provisioning.api.IntAttrName; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional("Master") +public class IntAttrNameParserTest extends AbstractTest { + + @Autowired + private IntAttrNameParser intAttrNameParser; + + @Test + public void ownFields() { + IntAttrName intAttrName = intAttrNameParser.parse("key", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind()); + assertNotNull(intAttrName.getField()); + assertEquals("key", intAttrName.getField()); + assertNull(intAttrName.getSchemaName()); + assertNull(intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + + intAttrName = intAttrNameParser.parse("name", AnyTypeKind.GROUP); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.GROUP, intAttrName.getAnyTypeKind()); + assertNotNull(intAttrName.getField()); + assertEquals("name", intAttrName.getField()); + assertNull(intAttrName.getSchemaName()); + assertNull(intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + + intAttrName = intAttrNameParser.parse("userOwner", AnyTypeKind.GROUP); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.GROUP, intAttrName.getAnyTypeKind()); + assertNotNull(intAttrName.getField()); + assertEquals("userOwner", intAttrName.getField()); + assertNull(intAttrName.getSchemaName()); + assertNull(intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + + intAttrName = intAttrNameParser.parse("name", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + } + + @Test + public void ownSchema() { + IntAttrName intAttrName = intAttrNameParser.parse("email", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + assertEquals("email", intAttrName.getSchemaName()); + assertEquals(SchemaType.PLAIN, intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + + intAttrName = intAttrNameParser.parse("cn", AnyTypeKind.ANY_OBJECT); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.ANY_OBJECT, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + assertEquals("cn", intAttrName.getSchemaName()); + assertEquals(SchemaType.DERIVED, intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + + intAttrName = intAttrNameParser.parse("rvirtualdata", AnyTypeKind.ANY_OBJECT); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.ANY_OBJECT, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + assertEquals("rvirtualdata", intAttrName.getSchemaName()); + assertEquals(SchemaType.VIRTUAL, intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + } + + @Test + public void enclosingGroup() { + IntAttrName intAttrName = intAttrNameParser.parse("groups[readers].cn", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.GROUP, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + assertEquals("cn", intAttrName.getSchemaName()); + assertEquals(SchemaType.DERIVED, intAttrName.getSchemaType()); + assertEquals("readers", intAttrName.getEnclosingGroup()); + assertNull(intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + } + + @Test + public void relatedAnyObject() { + IntAttrName intAttrName = intAttrNameParser.parse("anyObjects[hp].name", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.ANY_OBJECT, intAttrName.getAnyTypeKind()); + assertEquals("name", intAttrName.getField()); + assertNull(intAttrName.getSchemaName()); + assertNull(intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertEquals("hp", intAttrName.getRelatedAnyObject()); + assertNull(intAttrName.getMembershipOfGroup()); + } + + @Test + public void membership() { + IntAttrName intAttrName = intAttrNameParser.parse("memberships[top].cn", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + assertEquals("cn", intAttrName.getSchemaName()); + assertEquals(SchemaType.DERIVED, intAttrName.getSchemaType()); + assertNull(intAttrName.getEnclosingGroup()); + assertEquals("top", intAttrName.getMembershipOfGroup()); + assertNull(intAttrName.getRelatedAnyObject()); + } + + @Test + public void invalid() { + try { + intAttrNameParser.parse("memberships.cn", AnyTypeKind.USER); + fail(); + } catch (IllegalArgumentException e) { + assertNotNull(e); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java index dd6792b..0e84b68 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java @@ -27,6 +27,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.identityconnectors.framework.common.objects.Name; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -57,12 +58,12 @@ public class MappingTest extends AbstractTest { User user = userDAO.findByUsername("rossini"); assertNotNull(user); - Name name = MappingManagerImpl.evaluateNAME(user, provision, user.getUsername()); + Name name = MappingUtils.evaluateNAME(user, provision, user.getUsername()); assertEquals("uid=rossini,ou=people,o=isp", name.getNameValue()); provision.getMapping().setConnObjectLink("'uid=' + username + ',o=' + realm + ',ou=people,o=isp'"); - name = MappingManagerImpl.evaluateNAME(user, provision, user.getUsername()); + name = MappingUtils.evaluateNAME(user, provision, user.getUsername()); assertEquals("uid=rossini,o=even,ou=people,o=isp", name.getNameValue()); } } http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java index 7fc60df..822860f 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java @@ -34,11 +34,11 @@ public class PrefixMappingItemTransformer extends DefaultMappingItemTransformer @Override public List beforePropagation( final MappingItem mappingItem, - final List> anys, + final Any any, final List values) { if (values == null || values.isEmpty() || values.get(0).getStringValue() == null) { - return super.beforePropagation(mappingItem, anys, values); + return super.beforePropagation(mappingItem, any, values); } else { String value = values.get(0).getStringValue(); values.get(0).setStringValue(PREFIX + value); http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/test/java/org/apache/syncope/fit/console/NotificationsITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/NotificationsITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/NotificationsITCase.java index 1036fdc..849db31 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/NotificationsITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/NotificationsITCase.java @@ -43,17 +43,7 @@ public class NotificationsITCase extends AbstractConsoleITCase { FormTester formTester = wicketTester.newFormTester( "body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer:form"); - // ------------------------------- - // generate event to populate recipientAttrName - // ------------------------------- - formTester.setValue("content:form:view:recipientAttrType:dropDownChoiceField", "3"); - wicketTester.executeAjaxEvent("body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer:form:content:" - + "form:view:recipientAttrType:dropDownChoiceField", Constants.ON_CHANGE); - // ------------------------------- - - formTester.select("content:form:view:recipientAttrType:dropDownChoiceField", 3); - formTester.setValue("content:form:view:recipientAttrType:dropDownChoiceField", "3"); - formTester.setValue("content:form:view:recipientAttrName:dropDownChoiceField", "0"); + formTester.setValue("content:form:view:recipientAttrName:textField", "email"); formTester.select("content:form:view:template:dropDownChoiceField", 2); formTester.select("content:form:view:traceLevel:dropDownChoiceField", 0); formTester.setValue("content:form:view:sender:textField", sender); http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java index bd62785..f2b8f56 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java @@ -168,23 +168,12 @@ public class TopologyITCase extends AbstractConsoleITCase { formTester = wicketTester.newFormTester( "body:toggle:outerObjectsRepeater:3:outer:form:content:provision:container:content:wizard:form"); - formTester.setValue("view:mapping:mappingContainer:mappings:0:entities:dropDownChoiceField", "0"); - wicketTester.executeAjaxEvent( - "body:toggle:outerObjectsRepeater:3:outer:form:content:provision:container:content:wizard:form" - + ":view:mapping:mappingContainer:mappings:0:entities:dropDownChoiceField", Constants.ON_CHANGE); - - formTester.setValue("view:mapping:mappingContainer:mappings:0:intMappingTypes:dropDownChoiceField", "4"); - wicketTester.executeAjaxEvent( - "body:toggle:outerObjectsRepeater:3:outer:form:content:provision:container:content:wizard:form" - + ":view:mapping:mappingContainer:mappings:0:intMappingTypes:dropDownChoiceField", Constants.ON_CHANGE); - formTester.setValue("view:mapping:mappingContainer:mappings:0:connObjectKey:checkboxField", "true"); wicketTester.executeAjaxEvent( "body:toggle:outerObjectsRepeater:3:outer:form:content:provision:container:content:wizard:form" + ":view:mapping:mappingContainer:mappings:0:connObjectKey:checkboxField", Constants.ON_CHANGE); - formTester.setValue("view:mapping:mappingContainer:mappings:0:entities:dropDownChoiceField", "0"); - formTester.setValue("view:mapping:mappingContainer:mappings:0:intMappingTypes:dropDownChoiceField", "4"); + formTester.setValue("view:mapping:mappingContainer:mappings:0:intAttrName:textField", "key"); formTester.setValue("view:mapping:mappingContainer:mappings:0:extAttrName:textField", "ID"); formTester.setValue("view:mapping:mappingContainer:mappings:0:connObjectKey:checkboxField", "true"); http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java index a09b9bb..326cf96 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java @@ -867,8 +867,17 @@ public class GroupITCase extends AbstractITCase { @Test public void issueSYNCOPE632() { - GroupTO groupTO = null; + GroupTO groupTO = getSampleTO("lastGroup"); try { + // 0. create group + groupTO.getPlainAttrs().add(attrTO("icon", "anIcon")); + groupTO.getPlainAttrs().add(attrTO("show", "true")); + groupTO.getDerAttrs().add(attrTO("displayProperty", null)); + groupTO.getResources().clear(); + + groupTO = createGroup(groupTO).getAny(); + assertNotNull(groupTO); + // 1. create new LDAP resource having ConnObjectKey mapped to a derived attribute ResourceTO newLDAP = resourceService.read(RESOURCE_NAME_LDAP); newLDAP.setKey("new-ldap"); @@ -887,22 +896,22 @@ public class GroupITCase extends AbstractITCase { mapping.setConnObjectLink("'cn=' + displayProperty + ',ou=groups,o=isp'"); MappingItemTO description = new MappingItemTO(); + description.setIntAttrName("key"); description.setExtAttrName("description"); - description.setPurpose(MappingPurpose.BOTH); + description.setPurpose(MappingPurpose.PROPAGATION); mapping.add(description); newLDAP = createResource(newLDAP); assertNotNull(newLDAP); - // 2. create a group and give the resource created above - groupTO = getSampleTO("lastGroup" + getUUIDString()); - groupTO.getPlainAttrs().add(attrTO("icon", "anIcon")); - groupTO.getPlainAttrs().add(attrTO("show", "true")); - groupTO.getDerAttrs().add(attrTO("displayProperty", null)); - groupTO.getResources().clear(); - groupTO.getResources().add("new-ldap"); + // 2. update group and give the resource created above + GroupPatch patch = new GroupPatch(); + patch.setKey(groupTO.getKey()); + patch.getResources().add(new StringPatchItem.Builder(). + operation(PatchOperation.ADD_REPLACE). + value("new-ldap").build()); - groupTO = createGroup(groupTO).getAny(); + groupTO = updateGroup(patch).getAny(); assertNotNull(groupTO); // 3. update the group @@ -943,7 +952,7 @@ public class GroupITCase extends AbstractITCase { assertEquals(1, entries); } finally { - if (groupTO != null) { + if (groupTO.getKey() != null) { groupService.delete(groupTO.getKey()); } resourceService.delete("new-ldap"); http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java index 38b3631..3d98f28 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java @@ -18,30 +18,47 @@ */ package org.apache.syncope.fit.core; +import static org.apache.syncope.fit.core.AbstractTaskITCase.execProvisioningTask; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import javax.ws.rs.core.Response; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.IterableUtils; import org.apache.commons.collections4.Predicate; +import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.patch.AttrPatch; +import org.apache.syncope.common.lib.patch.DeassociationPatch; import org.apache.syncope.common.lib.patch.MembershipPatch; import org.apache.syncope.common.lib.patch.UserPatch; import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.BulkActionResult; +import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.MappingItemTO; import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.PullTaskTO; +import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.to.TypeExtensionTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.common.lib.types.PatchOperation; +import org.apache.syncope.common.lib.types.PropagationTaskExecStatus; +import org.apache.syncope.common.lib.types.ResourceDeassociationAction; +import org.apache.syncope.common.rest.api.beans.AnySearchQuery; +import org.apache.syncope.common.rest.api.service.TaskService; import org.apache.syncope.fit.AbstractITCase; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; +import org.springframework.jdbc.core.JdbcTemplate; @FixMethodOrder(MethodSorters.JVM) public class MembershipITCase extends AbstractITCase { @@ -203,4 +220,99 @@ public class MembershipITCase extends AbstractITCase { user = userService.read(user.getKey()); assertTrue(user.getMemberships().isEmpty()); } + + @Test + public void syncWithMembershipAttr() { + // 0. create ad-hoc resource, with adequate mapping + ResourceTO newResource = resourceService.read(RESOURCE_NAME_DBPULL); + newResource.setKey(getUUIDString()); + + MappingItemTO item = IterableUtils.find(newResource.getProvision("USER").getMapping().getItems(), + new Predicate() { + + @Override + public boolean evaluate(final MappingItemTO object) { + return "firstname".equals(object.getIntAttrName()); + } + }); + assertNotNull(item); + assertEquals("ID", item.getExtAttrName()); + item.setIntAttrName("memberships[additional].aLong"); + item.setPurpose(MappingPurpose.BOTH); + + item = IterableUtils.find(newResource.getProvision("USER").getMapping().getItems(), + new Predicate() { + + @Override + public boolean evaluate(final MappingItemTO object) { + return "fullname".equals(object.getIntAttrName()); + } + }); + item.setPurpose(MappingPurpose.PULL); + + PullTaskTO newTask = null; + try { + newResource = createResource(newResource); + assertNotNull(newResource); + + // 1. create user with new resource assigned + UserTO user = UserITCase.getUniqueSampleTO("memb@apache.org"); + user.setRealm("/even/two"); + user.getPlainAttrs().remove(user.getPlainAttrMap().get("ctype")); + user.getResources().clear(); + user.getResources().add(newResource.getKey()); + + MembershipTO membership = new MembershipTO.Builder().group("034740a9-fa10-453b-af37-dc7897e98fb1").build(); + membership.getPlainAttrs().add(new AttrTO.Builder().schema("aLong").value("5432").build()); + user.getMemberships().add(membership); + + user = createUser(user).getAny(); + assertNotNull(user); + + // 2. verify that user was found on resource + JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); + String idOnResource = jdbcTemplate.queryForObject( + "SELECT id FROM testpull WHERE id=?", String.class, "5432"); + assertEquals("5432", idOnResource); + + // 3. unlink user from resource, then remove it + DeassociationPatch patch = new DeassociationPatch(); + patch.setKey(user.getKey()); + patch.setAction(ResourceDeassociationAction.UNLINK); + patch.getResources().add(newResource.getKey()); + assertNotNull(userService.deassociate(patch).readEntity(BulkActionResult.class)); + + userService.delete(user.getKey()); + + // 4. create pull task and execute + newTask = taskService.read("7c2242f4-14af-4ab5-af31-cdae23783655", true); + newTask.setResource(newResource.getKey()); + newTask.setDestinationRealm("/even/two"); + + Response response = taskService.create(newTask); + newTask = getObject(response.getLocation(), TaskService.class, PullTaskTO.class); + assertNotNull(newTask); + + ExecTO execution = execProvisioningTask(taskService, newTask.getKey(), 50, false); + assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus())); + + // 5. verify that pulled user has + PagedResult users = userService.search(new AnySearchQuery.Builder(). + realm("/"). + fiql(SyncopeClient.getUserSearchConditionBuilder(). + is("username").equalTo(user.getUsername()).query()).build()); + assertEquals(1, users.getTotalCount()); + assertEquals(1, users.getResult().get(0).getMemberships().size()); + assertEquals("5432", users.getResult().get(0).getMemberships().get(0). + getPlainAttrMap().get("aLong").getValues().get(0)); + } catch (Exception e) { + LOG.error("Unexpected error", e); + fail(e.getMessage()); + } finally { + if (newTask != null && !"83f7e85d-9774-43fe-adba-ccd856312994".equals(newTask.getKey())) { + taskService.delete(newTask.getKey()); + } + resourceService.delete(newResource.getKey()); + } + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MigrationITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MigrationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MigrationITCase.java index a89fae3..59de26b 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MigrationITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MigrationITCase.java @@ -339,7 +339,7 @@ public class MigrationITCase extends AbstractTaskITCase { provisionTO.setMapping(mapping); item = new MappingItemTO(); - item.setIntAttrName("groupName"); + item.setIntAttrName("name"); item.setExtAttrName("name"); item.setMandatoryCondition("true"); item.setPurpose(MappingPurpose.PULL); @@ -417,7 +417,7 @@ public class MigrationITCase extends AbstractTaskITCase { public void migrateFromSyncope12() throws InterruptedException { // 1. cleanup try { - realmService.delete(MIGRATION_REALM); + realmService.delete("/" + MIGRATION_REALM); } catch (Exception e) { // ignore } http://git-wip-us.apache.org/repos/asf/syncope/blob/394f8c98/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java index f509611..6d7dd0d 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java @@ -255,10 +255,8 @@ public class ResourceITCase extends AbstractITCase { createResource(resourceTO); fail("Create should not have worked"); } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.Composite, e.getType()); - SyncopeClientException rvm = e.asComposite().getException(ClientExceptionType.RequiredValuesMissing); - assertNotNull(rvm); - assertEquals("intAttrName", rvm.getElements().iterator().next()); + assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType()); + assertEquals("intAttrName", e.getElements().iterator().next()); } } @@ -701,4 +699,34 @@ public class ResourceITCase extends AbstractITCase { } } } + + public void issueSYNCOPE645() { + ResourceTO resource = new ResourceTO(); + resource.setKey("ws-target-resource-basic-save-invalid"); + + String connector = resourceService.read("ws-target-resource-1").getConnector(); + resource.setConnector(connector); + + ProvisionTO provision = new ProvisionTO(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass("__ACCOUNT__"); + resource.getProvisions().add(provision); + + MappingTO mapping = new MappingTO(); + provision.setMapping(mapping); + + MappingItemTO item = new MappingItemTO(); + item.setIntAttrName("icon"); + item.setExtAttrName("icon"); + item.setPurpose(MappingPurpose.BOTH); + mapping.setConnObjectKeyItem(item); + + // save the resource + try { + resourceService.create(resource); + fail(); + } catch (SyncopeClientException e) { + assertEquals(ClientExceptionType.InvalidMapping, e.getType()); + } + } }