/* * 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.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.sql.DataSource; 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.AnyQuery; import org.apache.syncope.common.rest.api.service.TaskService; import org.apache.syncope.fit.AbstractITCase; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:testJDBCEnv.xml" }) public class MembershipITCase extends AbstractITCase { @Autowired private DataSource testDataSource; @Test public void misc() { UserTO user = UserITCase.getUniqueSampleTO("memb@apache.org"); user.setRealm("/even/two"); user.getPlainAttrs().add(new AttrTO.Builder().schema("aLong").value("1976").build()); user.getPlainAttrs().remove(user.getPlainAttrMap().get("ctype")); // the group 034740a9-fa10-453b-af37-dc7897e98fb1 has USER type extensions for 'csv' and 'other' // any type classes MembershipTO membership = new MembershipTO.Builder().group("034740a9-fa10-453b-af37-dc7897e98fb1").build(); membership.getPlainAttrs().add(new AttrTO.Builder().schema("aLong").value("1977").build()); // 'fullname' is in 'minimal user', so it is not allowed for this membership membership.getPlainAttrs().add(new AttrTO.Builder().schema("fullname").value("discarded").build()); user.getMemberships().add(membership); // user creation fails because of fullname try { createUser(user); fail(); } catch (SyncopeClientException e) { assertEquals(ClientExceptionType.InvalidUser, e.getType()); assertTrue(e.getMessage().contains("InvalidPlainAttr: fullname not allowed for membership of group")); } // remove fullname and try again CollectionUtils.filterInverse(membership.getPlainAttrs(), new Predicate<AttrTO>() { @Override public boolean evaluate(final AttrTO object) { return "fullname".equals(object.getSchema()); } }); try { user = createUser(user).getEntity(); // 1. verify that 'aLong' is correctly populated for user assertEquals(1, user.getPlainAttrMap().get("aLong").getValues().size()); assertEquals("1976", user.getPlainAttrMap().get("aLong").getValues().get(0)); // 2. verify that 'aLong' is correctly populated for user's membership assertEquals(1, user.getMemberships().size()); membership = user.getMembershipMap().get("034740a9-fa10-453b-af37-dc7897e98fb1"); assertNotNull(membership); assertEquals(1, membership.getPlainAttrMap().get("aLong").getValues().size()); assertEquals("1977", membership.getPlainAttrMap().get("aLong").getValues().get(0)); // 3. verify that derived attrbutes from 'csv' and 'other' are also populated for user's membership assertFalse(membership.getDerAttrMap().get("csvuserid").getValues().isEmpty()); assertFalse(membership.getDerAttrMap().get("noschema").getValues().isEmpty()); // update user - change some values and add new membership attribute UserPatch userPatch = new UserPatch(); userPatch.setKey(user.getKey()); userPatch.getPlainAttrs().add(new AttrPatch.Builder(). attrTO(new AttrTO.Builder().schema("aLong").value("1977").build()).build()); MembershipPatch membershipPatch = new MembershipPatch.Builder().group(membership.getGroupKey()).build(); membershipPatch.getPlainAttrs().add(new AttrPatch.Builder(). attrTO(new AttrTO.Builder().schema("aLong").value("1976").build()).build()); membershipPatch.getPlainAttrs().add(new AttrPatch.Builder(). attrTO(new AttrTO.Builder().schema("ctype").value("membership type").build()).build()); userPatch.getMemberships().add(membershipPatch); user = updateUser(userPatch).getEntity(); // 4. verify that 'aLong' is correctly populated for user assertEquals(1, user.getPlainAttrMap().get("aLong").getValues().size()); assertEquals("1977", user.getPlainAttrMap().get("aLong").getValues().get(0)); assertFalse(user.getPlainAttrMap().containsKey("ctype")); // 5. verify that 'aLong' is correctly populated for user's membership assertEquals(1, user.getMemberships().size()); membership = user.getMembershipMap().get("034740a9-fa10-453b-af37-dc7897e98fb1"); assertNotNull(membership); assertEquals(1, membership.getPlainAttrMap().get("aLong").getValues().size()); assertEquals("1976", membership.getPlainAttrMap().get("aLong").getValues().get(0)); // 6. verify that 'ctype' is correctly populated for user's membership assertEquals("membership type", membership.getPlainAttrMap().get("ctype").getValues().get(0)); // finally remove membership userPatch = new UserPatch(); userPatch.setKey(user.getKey()); membershipPatch = new MembershipPatch.Builder().group(membership.getGroupKey()). operation(PatchOperation.DELETE).build(); userPatch.getMemberships().add(membershipPatch); user = updateUser(userPatch).getEntity(); assertTrue(user.getMemberships().isEmpty()); } finally { if (user.getKey() != null) { userService.delete(user.getKey()); } } } @Test public void deleteUserWithMembership() { UserTO user = UserITCase.getUniqueSampleTO("memb@apache.org"); user.setRealm("/even/two"); user.getPlainAttrs().add(new AttrTO.Builder().schema("aLong").value("1976").build()); MembershipTO membership = new MembershipTO.Builder().group("034740a9-fa10-453b-af37-dc7897e98fb1").build(); membership.getPlainAttrs().add(new AttrTO.Builder().schema("aLong").value("1977").build()); user.getMemberships().add(membership); user = createUser(user).getEntity(); assertNotNull(user.getKey()); userService.delete(user.getKey()); } @Test public void onGroupDelete() { // pre: create group with type extension TypeExtensionTO typeExtension = new TypeExtensionTO(); typeExtension.setAnyType(AnyTypeKind.USER.name()); typeExtension.getAuxClasses().add("csv"); typeExtension.getAuxClasses().add("other"); GroupTO groupTO = GroupITCase.getBasicSampleTO("typeExt"); groupTO.getTypeExtensions().add(typeExtension); groupTO = createGroup(groupTO).getEntity(); assertNotNull(groupTO); // pre: create user with membership to such group UserTO user = UserITCase.getUniqueSampleTO("typeExt@apache.org"); MembershipTO membership = new MembershipTO.Builder().group(groupTO.getKey()).build(); membership.getPlainAttrs().add(new AttrTO.Builder().schema("aLong").value("1454").build()); user.getMemberships().add(membership); user = createUser(user).getEntity(); // verify that 'aLong' is correctly populated for user's membership assertEquals(1, user.getMemberships().size()); membership = user.getMembershipMap().get(groupTO.getKey()); assertNotNull(membership); assertEquals(1, membership.getPlainAttrMap().get("aLong").getValues().size()); assertEquals("1454", membership.getPlainAttrMap().get("aLong").getValues().get(0)); // verify that derived attrbutes from 'csv' and 'other' are also populated for user's membership assertFalse(membership.getDerAttrMap().get("csvuserid").getValues().isEmpty()); assertFalse(membership.getDerAttrMap().get("noschema").getValues().isEmpty()); // now remove the group -> all related memberships should have been removed as well groupService.delete(groupTO.getKey()); // re-read user and verify that no memberships are available any more user = userService.read(user.getKey()); assertTrue(user.getMemberships().isEmpty()); } @Test public void pull() { // 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<MappingItemTO>() { @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<MappingItemTO>() { @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).getEntity(); assertNotNull(user); // 2. verify that user was found on resource JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); String idOnResource = queryForObject( jdbcTemplate, 50, "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 = AbstractTaskITCase.execProvisioningTask(taskService, newTask.getKey(), 50, false); assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus())); // 5. verify that pulled user has PagedResult<UserTO> users = userService.search(new AnyQuery.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()); } } }