blob: 81df217b4ddbf76fd2651e94d314c65b17204c6c [file] [log] [blame]
/*
* 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;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.security.AccessControlException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
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.ForbiddenException;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.AnyOperations;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.request.AnyObjectCR;
import org.apache.syncope.common.lib.request.AnyObjectUR;
import org.apache.syncope.common.lib.request.ResourceAR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.ResourceDR;
import org.apache.syncope.common.lib.request.GroupUR;
import org.apache.syncope.common.lib.request.StringPatchItem;
import org.apache.syncope.common.lib.request.StringReplacePatchItem;
import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.AnyTypeClassTO;
import org.apache.syncope.common.lib.to.AnyTypeTO;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.ConnInstanceTO;
import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.DerSchemaTO;
import org.apache.syncope.common.lib.to.ExecTO;
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.ItemTO;
import org.apache.syncope.common.lib.to.MappingTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.PropagationStatus;
import org.apache.syncope.common.lib.to.ProvisionTO;
import org.apache.syncope.common.lib.to.ProvisioningResult;
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.AttrSchemaType;
import org.apache.syncope.common.lib.types.CipherAlgorithm;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.ConnectorCapability;
import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.common.lib.types.PatchOperation;
import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.common.lib.types.ProvisionAction;
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.lib.types.TaskType;
import org.apache.syncope.common.rest.api.beans.AnyQuery;
import org.apache.syncope.common.rest.api.service.GroupService;
import org.apache.syncope.common.rest.api.service.SyncopeService;
import org.apache.syncope.core.provisioning.java.job.TaskJob;
import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.fit.AbstractITCase;
import org.apache.syncope.fit.ElasticsearchDetector;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class GroupITCase extends AbstractITCase {
public static GroupCR getBasicSample(final String name) {
return new GroupCR.Builder(SyncopeConstants.ROOT_REALM, name + getUUIDString()).build();
}
public static GroupCR getSample(final String name) {
GroupCR groupCR = getBasicSample(name);
groupCR.getPlainAttrs().add(attr("icon", "anIcon"));
groupCR.getResources().add(RESOURCE_NAME_LDAP);
return groupCR;
}
@Test
public void create() {
GroupCR groupCR = getSample("lastGroup");
groupCR.getVirAttrs().add(attr("rvirtualdata", "rvirtualvalue"));
groupCR.setGroupOwner("f779c0d4-633b-4be5-8f57-32eb478a3ca5");
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
assertNotNull(groupTO.getVirAttr("rvirtualdata").get().getValues());
assertFalse(groupTO.getVirAttr("rvirtualdata").get().getValues().isEmpty());
assertEquals("rvirtualvalue", groupTO.getVirAttr("rvirtualdata").get().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.getAttr("owner"));
// SYNCOPE-515: remove ownership
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.setGroupOwner(new StringReplacePatchItem());
assertNull(updateGroup(groupUR).getEntity().getGroupOwner());
}
@Test
public void createWithInternationalCharacters() {
GroupCR groupCR = getSample("räksmörgås");
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
}
@Test
public void delete() {
try {
groupService.delete(UUID.randomUUID().toString());
} catch (SyncopeClientException e) {
assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
}
GroupCR groupCR = new GroupCR();
groupCR.setName("toBeDeleted" + getUUIDString());
groupCR.setRealm("/even");
groupCR.getResources().add(RESOURCE_NAME_LDAP);
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
GroupTO deletedGroup = deleteGroup(groupTO.getKey()).getEntity();
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.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).build());
assertNotNull(groupTOs);
assertTrue(groupTOs.getResult().size() >= 8);
groupTOs.getResult().forEach(Assertions::assertNotNull);
}
@Test
public void read() {
GroupTO groupTO = groupService.read("37d15e4c-cdc1-460b-a591-8505c8133806");
assertNotNull(groupTO);
assertNotNull(groupTO.getPlainAttrs());
assertFalse(groupTO.getPlainAttrs().isEmpty());
assertEquals(2, groupTO.getStaticUserMembershipCount());
}
@Test
public void selfRead() {
UserTO userTO = userService.read("1417acbe-cbf6-4277-9372-e75e04f97000");
assertNotNull(userTO);
assertTrue(userTO.getMembership("37d15e4c-cdc1-460b-a591-8505c8133806").isPresent());
assertFalse(userTO.getMembership("29f96485-729e-4d31-88a1-6fc60e4677f3").isPresent());
GroupService groupService2 = clientFactory.create("rossini", ADMIN_PWD).getService(GroupService.class);
try {
groupService2.read("29f96485-729e-4d31-88a1-6fc60e4677f3");
fail("This should not happen");
} catch (SyncopeClientException e) {
assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
}
List<GroupTO> groups = groupService2.own();
assertNotNull(groups);
assertTrue(groups.stream().anyMatch(group -> "37d15e4c-cdc1-460b-a591-8505c8133806".equals(group.getKey())));
}
@Test
public void update() {
GroupCR groupCR = getSample("latestGroup" + getUUIDString());
GroupTO groupTO = createGroup(groupCR).getEntity();
assertEquals(1, groupTO.getPlainAttrs().size());
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
String modName = "finalGroup" + getUUIDString();
groupUR.setName(new StringReplacePatchItem.Builder().value(modName).build());
groupUR.getPlainAttrs().add(attrAddReplacePatch("show", "FALSE"));
groupTO = updateGroup(groupUR).getEntity();
assertEquals(modName, groupTO.getName());
assertEquals(2, groupTO.getPlainAttrs().size());
groupTO.getPlainAttr("show").get().getValues().clear();
groupUR = new GroupUR.Builder(groupTO.getKey()).
plainAttr(new AttrPatch.Builder(new Attr.Builder("show").build()).
operation(PatchOperation.DELETE).build()).build();
groupTO = updateGroup(groupUR).getEntity();
assertFalse(groupTO.getPlainAttr("show").isPresent());
}
@Test
public void patch() {
GroupCR createReq = getBasicSample("patch");
createReq.setUDynMembershipCond("(($groups==3;$resources!=ws-target-resource-1);aLong==1)");
createReq.getADynMembershipConds().put(
PRINTER,
"(($groups==7;cool==ss);$resources==ws-target-resource-2);$type==PRINTER");
GroupTO created = createGroup(createReq).getEntity();
created.getPlainAttrs().add(new Attr.Builder("icon").build());
created.getPlainAttrs().add(new Attr.Builder("show").build());
created.getPlainAttrs().add(new Attr.Builder("rderived_sx").value("sx").build());
created.getPlainAttrs().add(new Attr.Builder("rderived_dx").value("dx").build());
created.getPlainAttrs().add(new Attr.Builder("title").value("mr").build());
GroupTO original = groupService.read(created.getKey());
GroupUR groupUR = AnyOperations.diff(created, original, true);
GroupTO updated = updateGroup(groupUR).getEntity();
Map<String, Attr> attrs = EntityTOUtils.buildAttrMap(updated.getPlainAttrs());
assertFalse(attrs.containsKey("icon"));
assertFalse(attrs.containsKey("show"));
assertEquals(List.of("sx"), attrs.get("rderived_sx").getValues());
assertEquals(List.of("dx"), attrs.get("rderived_dx").getValues());
assertEquals(List.of("mr"), attrs.get("title").getValues());
}
@Test
public void updateAsGroupOwner() {
// 1. read group as admin
GroupTO groupTO = groupService.read("ebf97068-aa4b-4a85-9f01-680e8c4cf227");
// issue SYNCOPE-15
assertNotNull(groupTO.getCreationDate());
assertNotNull(groupTO.getLastChangeDate());
assertEquals("admin", groupTO.getCreator());
assertEquals("admin", groupTO.getLastModifier());
// 2. prepare update
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.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(groupUR);
fail("This should not happen");
} catch (ForbiddenException 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(groupUR).readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
}).getEntity();
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 unlink() throws IOException {
GroupTO actual = createGroup(getSample("unlink")).getEntity();
assertNotNull(actual);
assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey()));
ResourceDR resourceDR = new ResourceDR.Builder().key(actual.getKey()).
action(ResourceDeassociationAction.UNLINK).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.deassociate(resourceDR)));
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() throws IOException {
GroupCR groupCR = getSample("link");
groupCR.getResources().clear();
GroupTO actual = createGroup(groupCR).getEntity();
assertNotNull(actual);
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
ResourceAR resourceAR = new ResourceAR.Builder().key(actual.getKey()).
action(ResourceAssociationAction.LINK).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.associate(resourceAR)));
actual = groupService.read(actual.getKey());
assertFalse(actual.getResources().isEmpty());
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), actual.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
}
@Test
public void unassign() throws IOException {
GroupTO groupTO = null;
try {
groupTO = createGroup(getSample("unassign")).getEntity();
assertNotNull(groupTO);
assertNotNull(resourceService.readConnObject(
RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey()));
ResourceDR resourceDR = new ResourceDR();
resourceDR.setKey(groupTO.getKey());
resourceDR.setAction(ResourceDeassociationAction.UNASSIGN);
resourceDR.getResources().add(RESOURCE_NAME_LDAP);
assertNotNull(parseBatchResponse(groupService.deassociate(resourceDR)));
groupTO = groupService.read(groupTO.getKey());
assertNotNull(groupTO);
assertTrue(groupTO.getResources().isEmpty());
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
} finally {
if (groupTO != null) {
groupService.delete(groupTO.getKey());
}
}
}
@Test
public void assign() throws IOException {
GroupCR groupCR = getSample("assign");
groupCR.getResources().clear();
GroupTO groupTO = null;
try {
groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
ResourceAR resourceAR = new ResourceAR.Builder().key(groupTO.getKey()).
action(ResourceAssociationAction.ASSIGN).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.associate(resourceAR)));
groupTO = groupService.read(groupTO.getKey());
assertFalse(groupTO.getResources().isEmpty());
assertNotNull(resourceService.readConnObject(
RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey()));
} finally {
if (groupTO != null) {
groupService.delete(groupTO.getKey());
}
}
}
@Test
public void deprovision() throws IOException {
GroupTO groupTO = null;
try {
groupTO = createGroup(getSample("deprovision")).getEntity();
assertNotNull(groupTO);
assertNotNull(groupTO.getKey());
assertNotNull(resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey()));
ResourceDR resourceDR = new ResourceDR.Builder().key(groupTO.getKey()).
action(ResourceDeassociationAction.DEPROVISION).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.deassociate(resourceDR)));
groupTO = groupService.read(groupTO.getKey());
assertNotNull(groupTO);
assertFalse(groupTO.getResources().isEmpty());
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
} finally {
if (groupTO != null) {
groupService.delete(groupTO.getKey());
}
}
}
@Test
public void provision() throws IOException {
GroupCR groupCR = getSample("provision");
groupCR.getResources().clear();
GroupTO groupTO = null;
try {
groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
ResourceAR resourceAR = new ResourceAR.Builder().key(groupTO.getKey()).
action(ResourceAssociationAction.PROVISION).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.associate(resourceAR)));
groupTO = groupService.read(groupTO.getKey());
assertTrue(groupTO.getResources().isEmpty());
assertNotNull(resourceService.readConnObject(
RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey()));
} finally {
if (groupTO != null) {
groupService.delete(groupTO.getKey());
}
}
}
@Test
public void deprovisionUnlinked() throws IOException {
GroupCR groupCR = getSample("deprovision");
groupCR.getResources().clear();
GroupTO groupTO = null;
try {
groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
ResourceAR resourceAR = new ResourceAR.Builder().key(groupTO.getKey()).
action(ResourceAssociationAction.PROVISION).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.associate(resourceAR)));
groupTO = groupService.read(groupTO.getKey());
assertTrue(groupTO.getResources().isEmpty());
assertNotNull(resourceService.readConnObject(
RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey()));
ResourceDR resourceDR = new ResourceDR.Builder().key(groupTO.getKey()).
action(ResourceDeassociationAction.DEPROVISION).resource(RESOURCE_NAME_LDAP).build();
assertNotNull(parseBatchResponse(groupService.deassociate(resourceDR)));
groupTO = groupService.read(groupTO.getKey());
assertNotNull(groupTO);
assertTrue(groupTO.getResources().isEmpty());
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
fail("This should not happen");
} catch (Exception e) {
assertNotNull(e);
}
} finally {
if (groupTO != null) {
groupService.delete(groupTO.getKey());
}
}
}
@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
GroupCR groupCR = getSample("lastGroup");
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
assertFalse(groupTO.getPlainAttr(badge.getKey()).isPresent());
// 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
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
try {
updateGroup(groupUR);
fail("This should not happen");
} catch (SyncopeClientException e) {
assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
}
// 5. also add an actual attribute for badge - it will work
groupUR.getPlainAttrs().add(attrAddReplacePatch(badge.getKey(), "xxxxxxxxxx"));
groupTO = updateGroup(groupUR).getEntity();
assertNotNull(groupTO);
assertNotNull(groupTO.getPlainAttr(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 encrypted() throws Exception {
// 1. create encrypted schema with secret key as system property
PlainSchemaTO encrypted = new PlainSchemaTO();
encrypted.setKey("encrypted" + getUUIDString());
encrypted.setType(AttrSchemaType.Encrypted);
encrypted.setCipherAlgorithm(CipherAlgorithm.SHA512);
encrypted.setSecretKey("${obscureSecretKey}");
schemaService.create(SchemaType.PLAIN, encrypted);
// 2. add the new 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(encrypted.getKey());
anyTypeClassService.update(typeClass);
typeClass = anyTypeClassService.read(typeClassName);
assertTrue(typeClass.getPlainSchemas().contains(encrypted.getKey()));
// 3. create group, verify that the correct encrypted value is returned
GroupCR groupCR = getSample("encrypted");
groupCR.getPlainAttrs().add(new Attr.Builder(encrypted.getKey()).value("testvalue").build());
GroupTO group = createGroup(groupCR).getEntity();
assertEquals(Encryptor.getInstance(System.getProperty("obscureSecretKey")).
encode("testvalue", encrypted.getCipherAlgorithm()),
group.getPlainAttr(encrypted.getKey()).get().getValues().get(0));
// 4. update schema to return cleartext values
encrypted.setAnyTypeClass(typeClassName);
encrypted.setCipherAlgorithm(CipherAlgorithm.AES);
encrypted.setConversionPattern(SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN);
schemaService.update(SchemaType.PLAIN, encrypted);
// 5. update group, verify that the cleartext value is returned
GroupUR groupUR = new GroupUR();
groupUR.setKey(group.getKey());
groupUR.getPlainAttrs().add(new AttrPatch.Builder(
new Attr.Builder(encrypted.getKey()).value("testvalue").build()).build());
group = updateGroup(groupUR).getEntity();
assertEquals("testvalue", group.getPlainAttr(encrypted.getKey()).get().getValues().get(0));
// 6. update schema again to disallow cleartext values
encrypted.setConversionPattern(null);
schemaService.update(SchemaType.PLAIN, encrypted);
group = groupService.read(group.getKey());
assertNotEquals("testvalue", group.getPlainAttr(encrypted.getKey()).get().getValues().get(0));
}
@Test
public void anonymous() {
GroupService unauthenticated = clientFactory.create().getService(GroupService.class);
try {
unauthenticated.search(new AnyQuery.Builder().realm("/even").build());
fail("This should not happen");
} catch (AccessControlException e) {
assertNotNull(e);
}
SyncopeClient anonymous = clientFactory.create(
new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
try {
anonymous.getService(GroupService.class).
search(new AnyQuery.Builder().realm("/even").build());
fail("This should not happen");
} catch (ForbiddenException e) {
assertNotNull(e);
}
assertFalse(anonymous.getService(SyncopeService.class).
searchAssignableGroups("/even", null, 1, 100).getResult().isEmpty());
}
@Test
public void uDynMembership() {
assertTrue(userService.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships().isEmpty());
GroupCR groupCR = getBasicSample("uDynMembership");
groupCR.setUDynMembershipCond("cool==true");
GroupTO group = createGroup(groupCR).getEntity();
assertNotNull(group);
final String groupKey = group.getKey();
List<MembershipTO> memberships = userService.read(
"c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships();
assertTrue(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
assertEquals(1, groupService.read(group.getKey()).getDynamicUserMembershipCount());
GroupUR groupUR = new GroupUR();
groupUR.setKey(group.getKey());
groupUR.setUDynMembershipCond("cool==false");
groupService.update(groupUR);
assertTrue(userService.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships().isEmpty());
assertEquals(0, groupService.read(group.getKey()).getDynamicUserMembershipCount());
}
@Test
public void aDynMembership() {
String fiql = SyncopeClient.getAnyObjectSearchConditionBuilder(PRINTER).is("location").notNullValue().query();
// 1. create group with a given aDynMembership condition
GroupCR groupCR = getBasicSample("aDynMembership");
groupCR.getADynMembershipConds().put(PRINTER, fiql);
GroupTO group = createGroup(groupCR).getEntity();
assertEquals(fiql, group.getADynMembershipConds().get(PRINTER));
group = groupService.read(group.getKey());
final String groupKey = group.getKey();
assertEquals(fiql, group.getADynMembershipConds().get(PRINTER));
// verify that the condition is dynamically applied
AnyObjectCR newAnyCR = AnyObjectITCase.getSample("aDynMembership");
newAnyCR.getResources().clear();
AnyObjectTO newAny = createAnyObject(newAnyCR).getEntity();
assertNotNull(newAny.getPlainAttr("location"));
List<MembershipTO> memberships = anyObjectService.read(
"fc6dbc3a-6c07-4965-8781-921e7401a4a5").getDynMemberships();
assertTrue(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
memberships = anyObjectService.read(
"8559d14d-58c2-46eb-a2d4-a7d35161e8f8").getDynMemberships();
assertTrue(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
memberships = anyObjectService.read(newAny.getKey()).getDynMemberships();
assertTrue(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
// 2. update group and change aDynMembership condition
fiql = SyncopeClient.getAnyObjectSearchConditionBuilder(PRINTER).is("location").nullValue().query();
GroupUR groupUR = new GroupUR();
groupUR.setKey(group.getKey());
groupUR.getADynMembershipConds().put(PRINTER, fiql);
group = updateGroup(groupUR).getEntity();
assertEquals(fiql, group.getADynMembershipConds().get(PRINTER));
group = groupService.read(group.getKey());
assertEquals(fiql, group.getADynMembershipConds().get(PRINTER));
// verify that the condition is dynamically applied
AnyObjectUR anyObjectUR = new AnyObjectUR();
anyObjectUR.setKey(newAny.getKey());
anyObjectUR.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder("location").build()).
operation(PatchOperation.DELETE).
build());
newAny = updateAnyObject(anyObjectUR).getEntity();
assertFalse(newAny.getPlainAttr("location").isPresent());
memberships = anyObjectService.read(
"fc6dbc3a-6c07-4965-8781-921e7401a4a5").getDynMemberships();
assertFalse(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
memberships = anyObjectService.read(
"8559d14d-58c2-46eb-a2d4-a7d35161e8f8").getDynMemberships();
assertFalse(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
memberships = anyObjectService.read(newAny.getKey()).getDynMemberships();
assertTrue(memberships.stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
}
@Test
public void aDynMembershipCount() {
// Create a new printer as a dynamic member of a new group
GroupCR groupCR = getBasicSample("aDynamicMembership");
String fiql = SyncopeClient.getAnyObjectSearchConditionBuilder(PRINTER).is("location").equalTo("home").query();
groupCR.getADynMembershipConds().put(PRINTER, fiql);
GroupTO group = createGroup(groupCR).getEntity();
AnyObjectCR printerCR = new AnyObjectCR();
printerCR.setRealm(SyncopeConstants.ROOT_REALM);
printerCR.setName("Printer_" + getUUIDString());
printerCR.setType(PRINTER);
printerCR.getPlainAttrs().add(new Attr.Builder("location").value("home").build());
AnyObjectTO printer = createAnyObject(printerCR).getEntity();
group = groupService.read(group.getKey());
assertEquals(0, group.getStaticAnyObjectMembershipCount());
assertEquals(1, group.getDynamicAnyObjectMembershipCount());
anyObjectService.delete(printer.getKey());
groupService.delete(group.getKey());
}
@Test
public void aStaticMembershipCount() {
// Create a new printer as a static member of a new group
GroupCR groupCR = getBasicSample("aStaticMembership");
GroupTO group = createGroup(groupCR).getEntity();
AnyObjectCR printerCR = new AnyObjectCR();
printerCR.setRealm(SyncopeConstants.ROOT_REALM);
printerCR.setName("Printer_" + getUUIDString());
printerCR.setType(PRINTER);
printerCR.getMemberships().add(new MembershipTO.Builder(group.getKey()).build());
AnyObjectTO printer = createAnyObject(printerCR).getEntity();
group = groupService.read(group.getKey());
assertEquals(0, group.getDynamicAnyObjectMembershipCount());
assertEquals(1, group.getStaticAnyObjectMembershipCount());
anyObjectService.delete(printer.getKey());
groupService.delete(group.getKey());
}
@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
GroupCR groupCR = getSample("syncope714");
groupCR.getPlainAttrs().add(attr("title", "first"));
groupCR.getResources().add(RESOURCE_NAME_LDAP);
ProvisioningResult<GroupTO> result = createGroup(groupCR);
assertNotNull(result);
assertEquals(1, result.getPropagationStatuses().size());
assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
GroupTO group = result.getEntity();
// 2. update succeeds
GroupUR groupUR = new GroupUR();
groupUR.setKey(group.getKey());
groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("title", "second")).
operation(PatchOperation.ADD_REPLACE).build());
result = updateGroup(groupUR);
assertNotNull(result);
assertEquals(1, result.getPropagationStatuses().size());
assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
group = result.getEntity();
// 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
groupUR = new GroupUR();
groupUR.setKey(group.getKey());
groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("title", "third")).
operation(PatchOperation.ADD_REPLACE).build());
result = updateGroup(groupUR);
assertNotNull(result);
assertEquals(1, result.getPropagationStatuses().size());
assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
group = result.getEntity();
// 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
groupUR = new GroupUR();
groupUR.setKey(group.getKey());
groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("title", "fourth")).
operation(PatchOperation.ADD_REPLACE).build());
result = updateGroup(groupUR);
assertNotNull(result);
assertEquals(1, result.getPropagationStatuses().size());
assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
assertEquals(ExecStatus.NOT_ATTEMPTED, result.getPropagationStatuses().get(0).getStatus());
} finally {
ldap.getCapabilitiesOverride().clear();
ldap.setOverrideCapabilities(false);
resourceService.update(ldap);
}
}
@Test
public void typeExtensions() {
TypeExtensionTO typeExtension = new TypeExtensionTO();
typeExtension.setAnyType(AnyTypeKind.USER.name());
typeExtension.getAuxClasses().add("csv");
GroupCR groupCR = getBasicSample("typeExtensions");
groupCR.getTypeExtensions().add(typeExtension);
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
assertEquals(1, groupTO.getTypeExtensions().size());
assertEquals(1, groupTO.getTypeExtension(AnyTypeKind.USER.name()).get().getAuxClasses().size());
assertTrue(groupTO.getTypeExtension(AnyTypeKind.USER.name()).get().getAuxClasses().contains("csv"));
typeExtension = new TypeExtensionTO();
typeExtension.setAnyType(AnyTypeKind.USER.name());
typeExtension.getAuxClasses().add("csv");
typeExtension.getAuxClasses().add("other");
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.getTypeExtensions().add(typeExtension);
groupTO = updateGroup(groupUR).getEntity();
assertNotNull(groupTO);
assertEquals(1, groupTO.getTypeExtensions().size());
assertEquals(2, groupTO.getTypeExtension(AnyTypeKind.USER.name()).get().getAuxClasses().size());
assertTrue(groupTO.getTypeExtension(AnyTypeKind.USER.name()).get().getAuxClasses().contains("csv"));
assertTrue(groupTO.getTypeExtension(AnyTypeKind.USER.name()).get().getAuxClasses().contains("other"));
}
@Test
public void provisionMembers() throws InterruptedException {
// 1. create group without resources
GroupCR groupCR = getBasicSample("forProvision");
GroupTO groupTO = createGroup(groupCR).getEntity();
// 2. create user with such group assigned
UserCR userCR = UserITCase.getUniqueSample("forProvision@syncope.apache.org");
userCR.getMemberships().add(new MembershipTO.Builder(groupTO.getKey()).build());
UserTO userTO = createUser(userCR).getEntity();
// 3. modify the group by assiging the LDAP resource
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_LDAP).build());
ProvisioningResult<GroupTO> groupUpdateResult = updateGroup(groupUR);
groupTO = groupUpdateResult.getEntity();
PropagationStatus propStatus = groupUpdateResult.getPropagationStatuses().get(0);
assertEquals(RESOURCE_NAME_LDAP, propStatus.getResource());
assertEquals(ExecStatus.SUCCESS, propStatus.getStatus());
// 4. verify that the user above is not found on LDAP
try {
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
fail("This should not happen");
} catch (SyncopeClientException e) {
assertEquals(ClientExceptionType.NotFound, e.getType());
}
try {
// 5. provision group members
ExecTO exec = groupService.provisionMembers(groupTO.getKey(), ProvisionAction.PROVISION);
assertNotNull(exec.getRefKey());
AtomicReference<List<ExecTO>> execs = new AtomicReference<>();
await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> {
try {
execs.set(taskService.read(TaskType.SCHEDULED, exec.getRefKey(), true).getExecutions());
return !execs.get().isEmpty();
} catch (Exception e) {
return false;
}
});
assertEquals(TaskJob.Status.SUCCESS.name(), execs.get().get(0).getStatus());
// 6. verify that the user above is now fond on LDAP
if (ElasticsearchDetector.isElasticSearchEnabled(adminClient.platform())) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
// ignore
}
}
ConnObjectTO userOnLdap =
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
assertNotNull(userOnLdap);
} finally {
groupService.delete(groupTO.getKey());
userService.delete(userTO.getKey());
}
}
@Test
public void issue178() {
GroupCR groupCR = new GroupCR();
String groupName = "torename" + getUUIDString();
groupCR.setName(groupName);
groupCR.setRealm("/");
GroupTO actual = createGroup(groupCR).getEntity();
assertNotNull(actual);
assertEquals(groupName, actual.getName());
GroupUR groupUR = new GroupUR();
groupUR.setKey(actual.getKey());
String renamedGroup = "renamed" + getUUIDString();
groupUR.setName(new StringReplacePatchItem.Builder().value(renamedGroup).build());
actual = updateGroup(groupUR).getEntity();
assertNotNull(actual);
assertEquals(renamedGroup, actual.getName());
}
@Test
public void issueSYNCOPE632() {
DerSchemaTO orig = schemaService.read(SchemaType.DERIVED, "displayProperty");
DerSchemaTO modified = SerializationUtils.clone(orig);
modified.setExpression("icon + '_' + show");
GroupCR groupCR = GroupITCase.getSample("lastGroup");
GroupTO groupTO = null;
try {
schemaService.update(SchemaType.DERIVED, modified);
// 0. create group
groupCR.getPlainAttrs().add(attr("icon", "anIcon"));
groupCR.getPlainAttrs().add(attr("show", "true"));
groupCR.getResources().clear();
groupTO = createGroup(groupCR).getEntity();
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");
newLDAP.setPropagationPriority(0);
for (ProvisionTO provision : newLDAP.getProvisions()) {
provision.getVirSchemas().clear();
}
MappingTO mapping = newLDAP.getProvision(AnyTypeKind.GROUP.name()).get().getMapping();
ItemTO connObjectKey = mapping.getConnObjectKeyItem();
connObjectKey.setIntAttrName("displayProperty");
connObjectKey.setPurpose(MappingPurpose.PROPAGATION);
mapping.setConnObjectKeyItem(connObjectKey);
mapping.setConnObjectLink("'cn=' + displayProperty + ',ou=groups,o=isp'");
ItemTO description = new ItemTO();
description.setIntAttrName("key");
description.setExtAttrName("description");
description.setPurpose(MappingPurpose.PROPAGATION);
mapping.add(description);
newLDAP = createResource(newLDAP);
assertNotNull(newLDAP);
// 2. update group and give the resource created above
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.getResources().add(new StringPatchItem.Builder().
operation(PatchOperation.ADD_REPLACE).
value("new-ldap").build());
groupTO = updateGroup(groupUR).getEntity();
assertNotNull(groupTO);
// 3. update the group
groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.getPlainAttrs().add(attrAddReplacePatch("icon", "anotherIcon"));
groupTO = updateGroup(groupUR).getEntity();
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 {
schemaService.update(SchemaType.DERIVED, orig);
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
GroupCR groupCR = GroupITCase.getBasicSample("syncope717");
groupCR.getPlainAttrs().add(attr(doubleSchemaName, "11.23"));
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
assertEquals("11.23", groupTO.getPlainAttr(doubleSchemaName).get().getValues().get(0));
// 3. update schema, set conversion pattern
schema = schemaService.read(SchemaType.PLAIN, schema.getKey());
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.getPlainAttr(doubleSchemaName).get().getValues().get(0));
// 5. modify group with new double value
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr(doubleSchemaName, "11.257")).build());
groupTO = updateGroup(groupUR).getEntity();
assertNotNull(groupTO);
assertEquals("11.257", groupTO.getPlainAttr(doubleSchemaName).get().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
groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr(doubleSchemaName, "11.23")).build());
groupTO = updateGroup(groupUR).getEntity();
assertNotNull(groupTO);
assertEquals("11.23", groupTO.getPlainAttr(doubleSchemaName).get().getValues().get(0));
}
@Test
public void issueSYNCOPE1467() {
GroupTO groupTO = null;
try {
GroupCR groupCR = new GroupCR();
groupCR.setRealm(SyncopeConstants.ROOT_REALM);
groupCR.setName("issueSYNCOPE1467");
groupCR.getResources().add(RESOURCE_NAME_LDAP);
groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
assertTrue(groupTO.getResources().contains(RESOURCE_NAME_LDAP));
ConnObjectTO connObjectTO =
resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
assertNotNull(connObjectTO);
assertEquals("issueSYNCOPE1467", connObjectTO.getAttr("cn").get().getValues().get(0));
GroupUR groupUR = new GroupUR();
groupUR.setKey(groupTO.getKey());
groupUR.setName(new StringReplacePatchItem.Builder().value("fixedSYNCOPE1467").build());
assertNotNull(updateGroup(groupUR).getEntity());
// Assert resources are present
ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
assertNotNull(ldap);
connObjectTO = resourceService.
readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
assertNotNull(connObjectTO);
assertEquals("fixedSYNCOPE1467", connObjectTO.getAttr("cn").get().getValues().get(0));
} finally {
if (groupTO.getKey() != null) {
groupService.delete(groupTO.getKey());
}
}
}
@Test
public void issueSYNCOPE1472() {
// 1. update group artDirector by assigning twice resource-testdb and auxiliary class csv
GroupUR groupUR = new GroupUR();
groupUR.setKey("ece66293-8f31-4a84-8e8d-23da36e70846");
groupUR.getResources().add(new StringPatchItem.Builder()
.value(RESOURCE_NAME_TESTDB)
.operation(PatchOperation.ADD_REPLACE)
.build());
groupUR.getAuxClasses().add(new StringPatchItem.Builder()
.operation(PatchOperation.ADD_REPLACE)
.value("csv")
.build());
for (int i = 0; i < 2; i++) {
updateGroup(groupUR);
}
// 2. remove resources and auxiliary classes
groupUR.getResources().clear();
groupUR.getResources().add(new StringPatchItem.Builder()
.value(RESOURCE_NAME_TESTDB)
.operation(PatchOperation.DELETE)
.build());
groupUR.getAuxClasses().clear();
groupUR.getAuxClasses().add(new StringPatchItem.Builder()
.value("csv")
.operation(PatchOperation.DELETE)
.build());
updateGroup(groupUR);
GroupTO groupTO = groupService.read("ece66293-8f31-4a84-8e8d-23da36e70846");
assertFalse(groupTO.getResources().contains(RESOURCE_NAME_TESTDB), "Should not contain removed resources");
assertFalse(groupTO.getAuxClasses().contains("csv"), "Should not contain removed auxiliary classes");
}
}