[SYNCOPE-717] Merge from 1_2_X
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/e486aaf3 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/e486aaf3 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/e486aaf3 Branch: refs/heads/SYNCOPE-156 Commit: e486aaf3a1ac1b5a3281a2c93598cc38cca4073e Parents: 0211410 5954e2e Author: Francesco Chicchiriccò <[email protected]> Authored: Thu Oct 29 10:37:23 2015 +0100 Committer: Francesco Chicchiriccò <[email protected]> Committed: Thu Oct 29 10:39:43 2015 +0100 ---------------------------------------------------------------------- .../syncope/core/misc/utils/FormatUtils.java | 6 +- .../syncope/fit/core/reference/GroupITCase.java | 59 ++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/e486aaf3/core/misc/src/main/java/org/apache/syncope/core/misc/utils/FormatUtils.java ---------------------------------------------------------------------- diff --cc core/misc/src/main/java/org/apache/syncope/core/misc/utils/FormatUtils.java index ec5250a,0000000..131f310 mode 100644,000000..100644 --- a/core/misc/src/main/java/org/apache/syncope/core/misc/utils/FormatUtils.java +++ b/core/misc/src/main/java/org/apache/syncope/core/misc/utils/FormatUtils.java @@@ -1,117 -1,0 +1,121 @@@ +/* + * 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.misc.utils; + +import java.text.DecimalFormat; ++import java.text.DecimalFormatSymbols; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; ++import java.util.Locale; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.syncope.common.lib.SyncopeConstants; + +/** + * Utility class for parsing / formatting date and numbers. + */ +public final class FormatUtils { + + private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() { + + @Override + protected SimpleDateFormat initialValue() { + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern(SyncopeConstants.DEFAULT_DATE_PATTERN); + return sdf; + } + }; + + private static final ThreadLocal<DecimalFormat> DECIMAL_FORMAT = new ThreadLocal<DecimalFormat>() { + + @Override + protected DecimalFormat initialValue() { - return new DecimalFormat(); ++ DecimalFormat df = new DecimalFormat(); ++ df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH)); ++ return df; + } + }; + + public static String format(final Date date) { + return format(date, true); + } + + public static String format(final Date date, final boolean lenient) { + return format(date, lenient, null); + } + + public static String format(final Date date, final boolean lenient, final String conversionPattern) { + SimpleDateFormat sdf = DATE_FORMAT.get(); + if (conversionPattern != null) { + sdf.applyPattern(conversionPattern); + } + sdf.setLenient(lenient); + return sdf.format(date); + } + + public static String format(final long number) { + return format(number, null); + } + + public static String format(final long number, final String conversionPattern) { + DecimalFormat df = DECIMAL_FORMAT.get(); + if (conversionPattern != null) { + df.applyPattern(conversionPattern); + } + return df.format(number); + } + + public static String format(final double number) { + return format(number, null); + } + + public static String format(final double number, final String conversionPattern) { + DecimalFormat df = DECIMAL_FORMAT.get(); + if (conversionPattern != null) { + df.applyPattern(conversionPattern); + } + return df.format(number); + } + + public static Date parseDate(final String source) throws ParseException { + return DateUtils.parseDate(source, SyncopeConstants.DATE_PATTERNS); + } + + public static Date parseDate(final String source, final String conversionPattern) throws ParseException { + SimpleDateFormat sdf = DATE_FORMAT.get(); + sdf.applyPattern(conversionPattern); + sdf.setLenient(false); + return sdf.parse(source); + } + + public static Number parseNumber(final String source, final String conversionPattern) throws ParseException { + DecimalFormat df = DECIMAL_FORMAT.get(); + df.applyPattern(conversionPattern); + return df.parse(source); + } + + public static void clear() { + DATE_FORMAT.remove(); + DECIMAL_FORMAT.remove(); + } + + private FormatUtils() { + // private empty constructor + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e486aaf3/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/GroupITCase.java ---------------------------------------------------------------------- diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/GroupITCase.java index db12a2b,0000000..17ba8c8 mode 100644,000000..100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/GroupITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/GroupITCase.java @@@ -1,806 -1,0 +1,865 @@@ +/* + * 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.fit.core.reference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessControlException; +import java.util.List; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.ws.rs.core.Response; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Predicate; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.client.lib.SyncopeClient; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.patch.AssociationPatch; +import org.apache.syncope.common.lib.patch.AttrPatch; +import org.apache.syncope.common.lib.patch.DeassociationPatch; +import org.apache.syncope.common.lib.patch.GroupPatch; +import org.apache.syncope.common.lib.patch.LongReplacePatchItem; +import org.apache.syncope.common.lib.patch.StringReplacePatchItem; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.common.lib.to.AnyTypeTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.BulkActionResult; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.to.ConnObjectTO; +import org.apache.syncope.common.lib.to.MappingItemTO; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.MappingTO; +import org.apache.syncope.common.lib.to.ProvisionTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; ++import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ConnectorCapability; +import org.apache.syncope.common.lib.types.IntMappingType; +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.ResourceAssociationAction; +import org.apache.syncope.common.lib.types.ResourceDeassociationAction; +import org.apache.syncope.common.lib.types.SchemaType; +import org.apache.syncope.common.rest.api.Preference; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.service.GroupService; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.JVM) +public class GroupITCase extends AbstractITCase { + + public static GroupTO getBasicSampleTO(final String name) { + GroupTO groupTO = new GroupTO(); + groupTO.setRealm("/"); + groupTO.setName(name + getUUIDString()); + return groupTO; + } + + public static GroupTO getSampleTO(final String name) { + GroupTO groupTO = getBasicSampleTO(name); + + groupTO.getPlainAttrs().add(attrTO("icon", "anIcon")); + + groupTO.getResources().add(RESOURCE_NAME_LDAP); + return groupTO; + } + + @Test + public void create() { + GroupTO groupTO = getSampleTO("lastGroup"); + groupTO.getVirAttrs().add(attrTO("rvirtualdata", "rvirtualvalue")); + groupTO.setGroupOwner(8L); + + groupTO = createGroup(groupTO); + assertNotNull(groupTO); + + assertNotNull(groupTO.getVirAttrMap()); + assertNotNull(groupTO.getVirAttrMap().get("rvirtualdata").getValues()); + assertFalse(groupTO.getVirAttrMap().get("rvirtualdata").getValues().isEmpty()); + assertEquals("rvirtualvalue", groupTO.getVirAttrMap().get("rvirtualdata").getValues().get(0)); + + assertTrue(groupTO.getResources().contains(RESOURCE_NAME_LDAP)); + + ConnObjectTO connObjectTO = + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey()); + assertNotNull(connObjectTO); + assertNotNull(connObjectTO.getPlainAttrMap().get("owner")); + + // SYNCOPE-515: remove ownership + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupTO.getKey()); + groupPatch.setGroupOwner(new LongReplacePatchItem()); + + assertNull(updateGroup(groupPatch).getGroupOwner()); + } + + @Test + public void delete() { + try { + groupService.delete(0L); + } catch (SyncopeClientException e) { + assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus()); + } + + GroupTO groupTO = new GroupTO(); + groupTO.setName("toBeDeleted" + getUUIDString()); + groupTO.setRealm("/even"); + + groupTO.getResources().add(RESOURCE_NAME_LDAP); + + groupTO = createGroup(groupTO); + assertNotNull(groupTO); + + GroupTO deletedGroup = deleteGroup(groupTO.getKey()); + assertNotNull(deletedGroup); + + try { + groupService.read(deletedGroup.getKey()); + } catch (SyncopeClientException e) { + assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus()); + } + } + + @Test + public void list() { + PagedResult<GroupTO> groupTOs = + groupService.list(SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).build()); + assertNotNull(groupTOs); + assertTrue(groupTOs.getResult().size() >= 8); + for (GroupTO groupTO : groupTOs.getResult()) { + assertNotNull(groupTO); + } + } + + @Test + public void read() { + GroupTO groupTO = groupService.read(1L); + + assertNotNull(groupTO); + assertNotNull(groupTO.getPlainAttrs()); + assertFalse(groupTO.getPlainAttrs().isEmpty()); + } + + @Test + public void selfRead() { + UserTO userTO = userService.read(1L); + assertNotNull(userTO); + + assertTrue(userTO.getMembershipMap().containsKey(1L)); + assertFalse(userTO.getMembershipMap().containsKey(3L)); + + GroupService groupService2 = clientFactory.create("rossini", ADMIN_PWD).getService(GroupService.class); + + try { + groupService2.read(3L); + fail(); + } catch (SyncopeClientException e) { + assertEquals(ClientExceptionType.DelegatedAdministration, e.getType()); + } + + List<GroupTO> groups = groupService2.own(); + assertNotNull(groups); + assertTrue(CollectionUtils.exists(groups, new Predicate<GroupTO>() { + + @Override + public boolean evaluate(final GroupTO group) { + return 1L == group.getKey(); + } + })); + } + + @Test + public void update() { + GroupTO groupTO = getSampleTO("latestGroup" + getUUIDString()); + groupTO = createGroup(groupTO); + + assertEquals(1, groupTO.getPlainAttrs().size()); + + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupTO.getKey()); + String modName = "finalGroup" + getUUIDString(); + groupPatch.setName(new StringReplacePatchItem.Builder().value(modName).build()); + groupPatch.getPlainAttrs().add(attrAddReplacePatch("show", "FALSE")); + + groupTO = updateGroup(groupPatch); + + assertEquals(modName, groupTO.getName()); + assertEquals(2, groupTO.getPlainAttrs().size()); + } + + @Test + public void updateRemovingDerAttribute() { + GroupTO groupTO = getBasicSampleTO("withderived" + getUUIDString()); + groupTO.getDerAttrs().add(attrTO("rderivedschema", null)); + + groupTO = createGroup(groupTO); + + assertNotNull(groupTO); + assertEquals(1, groupTO.getDerAttrs().size()); + + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupTO.getKey()); + groupPatch.getDerAttrs().add(new AttrPatch.Builder().operation(PatchOperation.DELETE). + attrTO(new AttrTO.Builder().schema("rderivedschema").build()). + build()); + + groupTO = updateGroup(groupPatch); + assertNotNull(groupTO); + assertTrue(groupTO.getDerAttrs().isEmpty()); + } + + @Test + public void updateAsGroupOwner() { + // 1. read group as admin + GroupTO groupTO = groupService.read(6L); + + // issue SYNCOPE-15 + assertNotNull(groupTO.getCreationDate()); + assertNotNull(groupTO.getLastChangeDate()); + assertEquals("admin", groupTO.getCreator()); + assertEquals("admin", groupTO.getLastModifier()); + + // 2. prepare update + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupTO.getKey()); + groupPatch.setName(new StringReplacePatchItem.Builder().value("Director").build()); + + // 3. try to update as verdi, not owner of group 6 - fail + GroupService groupService2 = clientFactory.create("verdi", ADMIN_PWD).getService(GroupService.class); + + try { + groupService2.update(groupPatch); + fail(); + } catch (SyncopeClientException e) { + assertEquals(Response.Status.UNAUTHORIZED, e.getType().getResponseStatus()); + } catch (AccessControlException e) { + assertNotNull(e); + } + + // 4. update as puccini, owner of group 6 - success + GroupService groupService3 = clientFactory.create("puccini", ADMIN_PWD).getService(GroupService.class); + + groupTO = groupService3.update(groupPatch).readEntity(GroupTO.class); + assertEquals("Director", groupTO.getName()); + + // issue SYNCOPE-15 + assertNotNull(groupTO.getCreationDate()); + assertNotNull(groupTO.getLastChangeDate()); + assertEquals("admin", groupTO.getCreator()); + assertEquals("puccini", groupTO.getLastModifier()); + assertTrue(groupTO.getCreationDate().before(groupTO.getLastChangeDate())); + } + + @Test + public void issue178() { + GroupTO groupTO = new GroupTO(); + String groupName = "torename" + getUUIDString(); + groupTO.setName(groupName); + groupTO.setRealm("/"); + + GroupTO actual = createGroup(groupTO); + + assertNotNull(actual); + assertEquals(groupName, actual.getName()); + + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(actual.getKey()); + String renamedGroup = "renamed" + getUUIDString(); + groupPatch.setName(new StringReplacePatchItem.Builder().value(renamedGroup).build()); + + actual = updateGroup(groupPatch); + assertNotNull(actual); + assertEquals(renamedGroup, actual.getName()); + } + + @Test + public void unlink() { + GroupTO actual = createGroup(getSampleTO("unlink")); + assertNotNull(actual); + + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + + DeassociationPatch deassociationPatch = new DeassociationPatch(); + deassociationPatch.setKey(actual.getKey()); + deassociationPatch.setAction(ResourceDeassociationAction.UNLINK); + deassociationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.deassociate(deassociationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertNotNull(actual); + assertTrue(actual.getResources().isEmpty()); + + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + } + + @Test + public void link() { + GroupTO groupTO = getSampleTO("link"); + groupTO.getResources().clear(); + + GroupTO actual = createGroup(groupTO); + assertNotNull(actual); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + + AssociationPatch associationPatch = new AssociationPatch(); + associationPatch.setKey(actual.getKey()); + associationPatch.setAction(ResourceAssociationAction.LINK); + associationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.associate(associationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertFalse(actual.getResources().isEmpty()); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + } + + @Test + public void unassign() { + GroupTO actual = createGroup(getSampleTO("unassign")); + assertNotNull(actual); + + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + + DeassociationPatch deassociationPatch = new DeassociationPatch(); + deassociationPatch.setKey(actual.getKey()); + deassociationPatch.setAction(ResourceDeassociationAction.UNASSIGN); + deassociationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.deassociate(deassociationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertNotNull(actual); + assertTrue(actual.getResources().isEmpty()); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + } + + @Test + public void assign() { + GroupTO groupTO = getSampleTO("assign"); + groupTO.getResources().clear(); + + GroupTO actual = createGroup(groupTO); + assertNotNull(actual); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + + AssociationPatch associationPatch = new AssociationPatch(); + associationPatch.setKey(actual.getKey()); + associationPatch.setAction(ResourceAssociationAction.ASSIGN); + associationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.associate(associationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertFalse(actual.getResources().isEmpty()); + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + } + + @Test + public void deprovision() { + GroupTO actual = createGroup(getSampleTO("deprovision")); + assertNotNull(actual); + assertNotNull(actual.getKey()); + + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + + DeassociationPatch deassociationPatch = new DeassociationPatch(); + deassociationPatch.setKey(actual.getKey()); + deassociationPatch.setAction(ResourceDeassociationAction.DEPROVISION); + deassociationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.deassociate(deassociationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertNotNull(actual); + assertFalse(actual.getResources().isEmpty()); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + } + + @Test + public void provision() { + GroupTO groupTO = getSampleTO("assign" + getUUIDString()); + groupTO.getResources().clear(); + + GroupTO actual = createGroup(groupTO); + assertNotNull(actual); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + + AssociationPatch associationPatch = new AssociationPatch(); + associationPatch.setKey(actual.getKey()); + associationPatch.setAction(ResourceAssociationAction.PROVISION); + associationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.associate(associationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertTrue(actual.getResources().isEmpty()); + + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + } + + @Test + public void deprovisionUnlinked() { + GroupTO groupTO = getSampleTO("assign" + getUUIDString()); + groupTO.getResources().clear(); + + GroupTO actual = createGroup(groupTO); + assertNotNull(actual); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + + AssociationPatch associationPatch = new AssociationPatch(); + associationPatch.setKey(actual.getKey()); + associationPatch.setAction(ResourceAssociationAction.PROVISION); + associationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.associate(associationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertTrue(actual.getResources().isEmpty()); + + assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey())); + + DeassociationPatch deassociationPatch = new DeassociationPatch(); + deassociationPatch.setKey(actual.getKey()); + deassociationPatch.setAction(ResourceDeassociationAction.DEPROVISION); + deassociationPatch.getResources().add(RESOURCE_NAME_LDAP); + + assertNotNull(groupService.deassociate(deassociationPatch).readEntity(BulkActionResult.class)); + + actual = groupService.read(actual.getKey()); + assertNotNull(actual); + assertTrue(actual.getResources().isEmpty()); + + try { + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()); + fail(); + } catch (Exception e) { + assertNotNull(e); + } + } + + @Test + public void createWithMandatorySchema() { + // 1. create a mandatory schema + PlainSchemaTO badge = new PlainSchemaTO(); + badge.setKey("badge" + getUUIDString()); + badge.setMandatoryCondition("true"); + schemaService.create(SchemaType.PLAIN, badge); + + // 2. create a group *without* an attribute for that schema: it works + GroupTO groupTO = getSampleTO("lastGroup"); + assertFalse(groupTO.getPlainAttrMap().containsKey(badge.getKey())); + groupTO = createGroup(groupTO); + assertNotNull(groupTO); + assertFalse(groupTO.getPlainAttrMap().containsKey(badge.getKey())); + + // 3. add the new mandatory schema to the default group type + AnyTypeTO type = anyTypeService.read(AnyTypeKind.GROUP.name()); + String typeClassName = type.getClasses().get(0); + AnyTypeClassTO typeClass = anyTypeClassService.read(typeClassName); + typeClass.getPlainSchemas().add(badge.getKey()); + anyTypeClassService.update(typeClass); + typeClass = anyTypeClassService.read(typeClassName); + assertTrue(typeClass.getPlainSchemas().contains(badge.getKey())); + + try { + // 4. update group: failure since no values are provided and it is mandatory + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupTO.getKey()); + + try { + updateGroup(groupPatch); + fail(); + } catch (SyncopeClientException e) { + assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType()); + } + + // 5. also add an actual attribute for badge - it will work + groupPatch.getPlainAttrs().add(attrAddReplacePatch(badge.getKey(), "xxxxxxxxxx")); + + groupTO = updateGroup(groupPatch); + assertNotNull(groupTO); + assertTrue(groupTO.getPlainAttrMap().containsKey(badge.getKey())); + } finally { + // restore the original group class + typeClass.getPlainSchemas().remove(badge.getKey()); + anyTypeClassService.update(typeClass); + typeClass = anyTypeClassService.read(typeClassName); + assertFalse(typeClass.getPlainSchemas().contains(badge.getKey())); + } + } + + @Test + public void anonymous() { + GroupService unauthenticated = clientFactory.create().getService(GroupService.class); + try { + unauthenticated. + list(SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).build()); + fail(); + } catch (AccessControlException e) { + assertNotNull(e); + } + + GroupService anonymous = clientFactory.create(ANONYMOUS_UNAME, ANONYMOUS_KEY).getService(GroupService.class); + assertFalse(anonymous.list(SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM). + build()). + getResult().isEmpty()); + } + + @Test + public void noContent() throws IOException { + SyncopeClient noContentclient = clientFactory.create(ADMIN_UNAME, ADMIN_PWD); + GroupService noContentService = noContentclient.prefer(GroupService.class, Preference.RETURN_NO_CONTENT); + + GroupTO group = getSampleTO("noContent"); + + Response response = noContentService.create(group); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED)); + assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity())); + + group = getObject(response.getLocation(), GroupService.class, GroupTO.class); + assertNotNull(group); + + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(group.getKey()); + groupPatch.getPlainAttrs().add(attrAddReplacePatch("badge", "xxxxxxxxxx")); + + response = noContentService.update(groupPatch); + assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus()); + assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED)); + assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity())); + + response = noContentService.delete(group.getKey()); + assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus()); + assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED)); + assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity())); + } + + @Test + public void dynMembership() { + assertTrue(userService.read(4L).getDynGroups().isEmpty()); + + GroupTO group = getBasicSampleTO("dynMembership"); + group.setUDynMembershipCond("cool==true"); + group = createGroup(group); + assertNotNull(group); + + assertTrue(userService.read(4L).getDynGroups().contains(group.getKey())); + + GroupPatch mod = new GroupPatch(); + mod.setKey(group.getKey()); + mod.setUDynMembershipCond(new StringReplacePatchItem.Builder().value("cool==false").build()); + groupService.update(mod); + + assertTrue(userService.read(4L).getDynGroups().isEmpty()); + } + + @Test + public void capabilitiesOverride() { + // resource with no capability override + ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP); + assertNotNull(ldap); + assertFalse(ldap.isOverrideCapabilities()); + assertTrue(ldap.getCapabilitiesOverride().isEmpty()); + + // connector with all required for create and update + ConnInstanceTO conn = connectorService.read(ldap.getConnector(), null); + assertNotNull(conn); + assertTrue(conn.getCapabilities().contains(ConnectorCapability.CREATE)); + assertTrue(conn.getCapabilities().contains(ConnectorCapability.UPDATE)); + + try { + // 1. create succeeds + GroupTO group = getSampleTO("syncope714"); + group.getPlainAttrs().add(attrTO("title", "first")); + group.getResources().add(RESOURCE_NAME_LDAP); + + group = createGroup(group); + assertNotNull(group); + assertEquals(1, group.getPropagationStatusTOs().size()); + assertEquals(RESOURCE_NAME_LDAP, group.getPropagationStatusTOs().get(0).getResource()); + assertEquals(PropagationTaskExecStatus.SUCCESS, group.getPropagationStatusTOs().get(0).getStatus()); + + // 2. update succeeds + GroupPatch patch = new GroupPatch(); + patch.setKey(group.getKey()); + patch.getPlainAttrs().add(new AttrPatch.Builder(). + operation(PatchOperation.ADD_REPLACE).attrTO(attrTO("title", "second")).build()); + + group = groupService.update(patch).readEntity(GroupTO.class); + assertNotNull(group); + assertEquals(1, group.getPropagationStatusTOs().size()); + assertEquals(RESOURCE_NAME_LDAP, group.getPropagationStatusTOs().get(0).getResource()); + assertEquals(PropagationTaskExecStatus.SUCCESS, group.getPropagationStatusTOs().get(0).getStatus()); + + // 3. set capability override with only search allowed, but not enable + ldap.getCapabilitiesOverride().add(ConnectorCapability.SEARCH); + resourceService.update(ldap); + ldap = resourceService.read(RESOURCE_NAME_LDAP); + assertNotNull(ldap); + assertFalse(ldap.isOverrideCapabilities()); + assertEquals(1, ldap.getCapabilitiesOverride().size()); + assertTrue(ldap.getCapabilitiesOverride().contains(ConnectorCapability.SEARCH)); + + // 4. update succeeds again + patch = new GroupPatch(); + patch.setKey(group.getKey()); + patch.getPlainAttrs().add(new AttrPatch.Builder(). + operation(PatchOperation.ADD_REPLACE).attrTO(attrTO("title", "third")).build()); + + group = groupService.update(patch).readEntity(GroupTO.class); + assertNotNull(group); + assertEquals(1, group.getPropagationStatusTOs().size()); + assertEquals(RESOURCE_NAME_LDAP, group.getPropagationStatusTOs().get(0).getResource()); + assertEquals(PropagationTaskExecStatus.SUCCESS, group.getPropagationStatusTOs().get(0).getStatus()); + + // 5. enable capability override + ldap.setOverrideCapabilities(true); + resourceService.update(ldap); + ldap = resourceService.read(RESOURCE_NAME_LDAP); + assertNotNull(ldap); + assertTrue(ldap.isOverrideCapabilities()); + assertEquals(1, ldap.getCapabilitiesOverride().size()); + assertTrue(ldap.getCapabilitiesOverride().contains(ConnectorCapability.SEARCH)); + + // 6. update now fails + patch = new GroupPatch(); + patch.setKey(group.getKey()); + patch.getPlainAttrs().add(new AttrPatch.Builder(). + operation(PatchOperation.ADD_REPLACE).attrTO(attrTO("title", "fourth")).build()); + + group = groupService.update(patch).readEntity(GroupTO.class); + assertNotNull(group); + assertEquals(1, group.getPropagationStatusTOs().size()); + assertEquals(RESOURCE_NAME_LDAP, group.getPropagationStatusTOs().get(0).getResource()); + assertEquals(PropagationTaskExecStatus.NOT_ATTEMPTED, group.getPropagationStatusTOs().get(0).getStatus()); + } finally { + ldap.getCapabilitiesOverride().clear(); + ldap.setOverrideCapabilities(false); + resourceService.update(ldap); + } + } + + @Test + public void issueSYNCOPE632() { + GroupTO groupTO = null; + try { + // 1. create new LDAP resource having ConnObjectKey mapped to a derived attribute + ResourceTO newLDAP = resourceService.read(RESOURCE_NAME_LDAP); + newLDAP.setKey("new-ldap"); + newLDAP.setPropagationPrimary(true); + + for (ProvisionTO provision : newLDAP.getProvisions()) { + provision.getVirSchemas().clear(); + } + + MappingTO mapping = newLDAP.getProvision(AnyTypeKind.GROUP.name()).getMapping(); + + MappingItemTO connObjectKey = mapping.getConnObjectKeyItem(); + connObjectKey.setIntMappingType(IntMappingType.GroupDerivedSchema); + connObjectKey.setIntAttrName("displayProperty"); + mapping.setConnObjectKeyItem(connObjectKey); + mapping.setConnObjectLink("'cn=' + displayProperty + ',ou=groups,o=isp'"); + + MappingItemTO description = new MappingItemTO(); + description.setIntMappingType(IntMappingType.GroupKey); + description.setExtAttrName("description"); + description.setPurpose(MappingPurpose.BOTH); + 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"); + + groupTO = createGroup(groupTO); + assertNotNull(groupTO); + + // 3. update the group + GroupPatch groupPatch = new GroupPatch(); + groupPatch.setKey(groupTO.getKey()); + groupPatch.getPlainAttrs().add(attrAddReplacePatch("icon", "anotherIcon")); + + groupTO = updateGroup(groupPatch); + assertNotNull(groupTO); + + // 4. check that a single group exists in LDAP for the group created and updated above + int entries = 0; + DirContext ctx = null; + try { + ctx = getLdapResourceDirContext(null, null); + + SearchControls ctls = new SearchControls(); + ctls.setReturningAttributes(new String[] { "*", "+" }); + ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + NamingEnumeration<SearchResult> result = + ctx.search("ou=groups,o=isp", "(description=" + groupTO.getKey() + ")", ctls); + while (result.hasMore()) { + result.next(); + entries++; + } + } catch (Exception e) { + // ignore + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (NamingException e) { + // ignore + } + } + } + + assertEquals(1, entries); + } finally { + if (groupTO != null) { + groupService.delete(groupTO.getKey()); + } + resourceService.delete("new-ldap"); + } + } + ++ @Test ++ public void issueSYNCOPE717() { ++ String doubleSchemaName = "double" + getUUIDString(); ++ ++ // 1. create double schema without conversion pattern ++ PlainSchemaTO schema = new PlainSchemaTO(); ++ schema.setKey(doubleSchemaName); ++ schema.setType(AttrSchemaType.Double); ++ ++ schema = createSchema(SchemaType.PLAIN, schema); ++ assertNotNull(schema); ++ assertNull(schema.getConversionPattern()); ++ ++ AnyTypeClassTO minimalGroup = anyTypeClassService.read("minimal group"); ++ assertNotNull(minimalGroup); ++ minimalGroup.getPlainSchemas().add(doubleSchemaName); ++ anyTypeClassService.update(minimalGroup); ++ ++ // 2. create group, provide valid input value ++ GroupTO groupTO = getBasicSampleTO("syncope717"); ++ groupTO.getPlainAttrs().add(attrTO(doubleSchemaName, "11.23")); ++ ++ groupTO = createGroup(groupTO); ++ assertNotNull(groupTO); ++ assertEquals("11.23", groupTO.getPlainAttrMap().get(doubleSchemaName).getValues().get(0)); ++ ++ // 3. update schema, set conversion pattern ++ schema.setConversionPattern("0.000"); ++ schemaService.update(SchemaType.PLAIN, schema); ++ ++ // 4. re-read group, verify that pattern was applied ++ groupTO = groupService.read(groupTO.getKey()); ++ assertNotNull(groupTO); ++ assertEquals("11.230", groupTO.getPlainAttrMap().get(doubleSchemaName).getValues().get(0)); ++ ++ // 5. modify group with new double value ++ GroupPatch patch = new GroupPatch(); ++ patch.setKey(groupTO.getKey()); ++ patch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO(doubleSchemaName, "11.257")).build()); ++ ++ groupTO = updateGroup(patch); ++ assertNotNull(groupTO); ++ assertEquals("11.257", groupTO.getPlainAttrMap().get(doubleSchemaName).getValues().get(0)); ++ ++ // 6. update schema, unset conversion pattern ++ schema.setConversionPattern(null); ++ schemaService.update(SchemaType.PLAIN, schema); ++ ++ // 7. modify group with new double value, verify that no pattern is applied ++ patch = new GroupPatch(); ++ patch.setKey(groupTO.getKey()); ++ patch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO(doubleSchemaName, "11.23")).build()); ++ ++ groupTO = updateGroup(patch); ++ assertNotNull(groupTO); ++ assertEquals("11.23", groupTO.getPlainAttrMap().get(doubleSchemaName).getValues().get(0)); ++ } ++ +}
