blob: 324129156cfc218cc61f1a7972c5bb9fd48caac3 [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.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import javax.ws.rs.core.Response;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.request.AnyObjectCR;
import org.apache.syncope.common.lib.request.AnyObjectUR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.MembershipUR;
import org.apache.syncope.common.lib.request.ResourceDR;
import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.ItemTO;
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.ExecStatus;
import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.common.lib.types.PatchOperation;
import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
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.TaskService;
import org.apache.syncope.fit.AbstractITCase;
import org.apache.syncope.fit.ElasticsearchDetector;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
public class MembershipITCase extends AbstractITCase {
@Test
public void misc() {
UserCR userCR = UserITCase.getUniqueSample("memb@apache.org");
userCR.setRealm("/even/two");
userCR.getPlainAttrs().add(new Attr.Builder("aLong").value("1976").build());
userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema()));
// the group 034740a9-fa10-453b-af37-dc7897e98fb1 has USER type extensions for 'csv' and 'other'
// any type classes
MembershipTO membership = new MembershipTO.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build();
membership.getPlainAttrs().add(new Attr.Builder("aLong").value("1977").build());
// 'fullname' is in 'minimal user', so it is not allowed for this membership
membership.getPlainAttrs().add(new Attr.Builder("fullname").value("discarded").build());
userCR.getMemberships().add(membership);
// user creation fails because of fullname
try {
createUser(userCR);
fail("This should not happen");
} catch (SyncopeClientException e) {
assertEquals(ClientExceptionType.InvalidEntity, e.getType());
assertTrue(e.getMessage().contains("InvalidPlainAttr: fullname not allowed for membership of group"));
}
// remove fullname and try again
membership.getPlainAttrs().remove(membership.getPlainAttr("fullname").get());
UserTO userTO = null;
try {
userTO = createUser(userCR).getEntity();
// 1. verify that 'aLong' is correctly populated for user
assertEquals(1, userTO.getPlainAttr("aLong").get().getValues().size());
assertEquals("1976", userTO.getPlainAttr("aLong").get().getValues().get(0));
// 2. verify that 'aLong' is correctly populated for user's membership
assertEquals(1, userCR.getMemberships().size());
membership = userTO.getMembership("034740a9-fa10-453b-af37-dc7897e98fb1").get();
assertNotNull(membership);
assertEquals(1, membership.getPlainAttr("aLong").get().getValues().size());
assertEquals("1977", membership.getPlainAttr("aLong").get().getValues().get(0));
// 3. verify that derived attrbutes from 'csv' and 'other' are also populated for user's membership
assertFalse(membership.getDerAttr("csvuserid").get().getValues().isEmpty());
assertFalse(membership.getDerAttr("noschema").get().getValues().isEmpty());
// update user - change some values and add new membership attribute
UserUR userUR = new UserUR();
userUR.setKey(userTO.getKey());
userUR.getPlainAttrs().
add(new AttrPatch.Builder(new Attr.Builder("aLong").value("1977").build()).build());
MembershipUR membershipPatch = new MembershipUR.Builder(membership.getGroupKey()).build();
membershipPatch.getPlainAttrs().add(new Attr.Builder("aLong").value("1976").build());
membershipPatch.getPlainAttrs().add(new Attr.Builder("ctype").value("membership type").build());
userUR.getMemberships().add(membershipPatch);
userTO = updateUser(userUR).getEntity();
// 4. verify that 'aLong' is correctly populated for user
assertEquals(1, userTO.getPlainAttr("aLong").get().getValues().size());
assertEquals("1977", userTO.getPlainAttr("aLong").get().getValues().get(0));
assertFalse(userTO.getPlainAttr("ctype").isPresent());
// 5. verify that 'aLong' is correctly populated for user's membership
assertEquals(1, userCR.getMemberships().size());
membership = userTO.getMembership("034740a9-fa10-453b-af37-dc7897e98fb1").get();
assertNotNull(membership);
assertEquals(1, membership.getPlainAttr("aLong").get().getValues().size());
assertEquals("1976", membership.getPlainAttr("aLong").get().getValues().get(0));
// 6. verify that 'ctype' is correctly populated for user's membership
assertEquals("membership type", membership.getPlainAttr("ctype").get().getValues().get(0));
// finally remove membership
userUR = new UserUR();
userUR.setKey(userTO.getKey());
membershipPatch = new MembershipUR.Builder(membership.getGroupKey()).
operation(PatchOperation.DELETE).build();
userUR.getMemberships().add(membershipPatch);
userTO = updateUser(userUR).getEntity();
assertTrue(userTO.getMemberships().isEmpty());
} finally {
if (userTO != null) {
USER_SERVICE.delete(userTO.getKey());
}
}
}
@Test
public void deleteUserWithMembership() {
UserCR userCR = UserITCase.getUniqueSample("memb@apache.org");
userCR.setRealm("/even/two");
userCR.getPlainAttrs().add(new Attr.Builder("aLong").value("1976").build());
MembershipTO membership = new MembershipTO.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build();
membership.getPlainAttrs().add(new Attr.Builder("aLong").value("1977").build());
userCR.getMemberships().add(membership);
UserTO user = createUser(userCR).getEntity();
assertNotNull(user.getKey());
USER_SERVICE.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");
GroupCR groupCR = GroupITCase.getBasicSample("typeExt");
groupCR.getTypeExtensions().add(typeExtension);
GroupTO groupTO = createGroup(groupCR).getEntity();
assertNotNull(groupTO);
// pre: create user with membership to such group
UserCR userCR = UserITCase.getUniqueSample("typeExt@apache.org");
MembershipTO membership = new MembershipTO.Builder(groupTO.getKey()).build();
membership.getPlainAttrs().add(new Attr.Builder("aLong").value("1454").build());
userCR.getMemberships().add(membership);
UserTO user = createUser(userCR).getEntity();
// verify that 'aLong' is correctly populated for user's membership
assertEquals(1, user.getMemberships().size());
membership = user.getMembership(groupTO.getKey()).get();
assertNotNull(membership);
assertEquals(1, membership.getPlainAttr("aLong").get().getValues().size());
assertEquals("1454", membership.getPlainAttr("aLong").get().getValues().get(0));
// verify that derived attrbutes from 'csv' and 'other' are also populated for user's membership
assertFalse(membership.getDerAttr("csvuserid").get().getValues().isEmpty());
assertFalse(membership.getDerAttr("noschema").get().getValues().isEmpty());
// now remove the group -> all related memberships should have been removed as well
GROUP_SERVICE.delete(groupTO.getKey());
// re-read user and verify that no memberships are available any more
user = USER_SERVICE.read(user.getKey());
assertTrue(user.getMemberships().isEmpty());
}
@Test
public void pull() {
// 0. create ad-hoc resource, with adequate mapping
ResourceTO newResource = RESOURCE_SERVICE.read(RESOURCE_NAME_DBPULL);
newResource.setKey(getUUIDString());
ItemTO item = newResource.getProvision("USER").get().getMapping().getItems().stream().
filter(object -> "firstname".equals(object.getIntAttrName())).findFirst().get();
assertNotNull(item);
assertEquals("ID", item.getExtAttrName());
item.setIntAttrName("memberships[additional].aLong");
item.setPurpose(MappingPurpose.BOTH);
item = newResource.getProvision("USER").get().getMapping().getItems().stream().
filter(object -> "fullname".equals(object.getIntAttrName())).findFirst().get();
item.setPurpose(MappingPurpose.PULL);
PullTaskTO newTask = null;
try {
newResource = createResource(newResource);
assertNotNull(newResource);
// 1. create user with new resource assigned
UserCR userCR = UserITCase.getUniqueSample("memb@apache.org");
userCR.setRealm("/even/two");
UserTO user;
userCR.getPlainAttrs().removeIf(attr -> "ctype".equals(attr.getSchema()));
userCR.getResources().clear();
userCR.getResources().add(newResource.getKey());
MembershipTO membership = new MembershipTO.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build();
membership.getPlainAttrs().add(new Attr.Builder("aLong").value("5432").build());
userCR.getMemberships().add(membership);
user = createUser(userCR).getEntity();
assertNotNull(user);
// 2. verify that user was found on resource
JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
String idOnResource = queryForObject(
jdbcTemplate, MAX_WAIT_SECONDS, "SELECT id FROM testpull WHERE id=?", String.class, "5432");
assertEquals("5432", idOnResource);
// 3. unlink user from resource, then remove it
ResourceDR req = new ResourceDR();
req.setKey(user.getKey());
req.setAction(ResourceDeassociationAction.UNLINK);
req.getResources().add(newResource.getKey());
assertNotNull(parseBatchResponse(USER_SERVICE.deassociate(req)));
USER_SERVICE.delete(user.getKey());
// 4. create pull task and execute
newTask = TASK_SERVICE.read(TaskType.PULL, "7c2242f4-14af-4ab5-af31-cdae23783655", true);
newTask.setResource(newResource.getKey());
newTask.setDestinationRealm("/even/two");
Response response = TASK_SERVICE.create(TaskType.PULL, newTask);
newTask = getObject(response.getLocation(), TaskService.class, PullTaskTO.class);
assertNotNull(newTask);
ExecTO execution = AbstractTaskITCase.execProvisioningTask(
TASK_SERVICE, TaskType.PULL, newTask.getKey(), MAX_WAIT_SECONDS, false);
assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus()));
// 5. verify that pulled user has
if (ElasticsearchDetector.isElasticSearchEnabled(ADMIN_CLIENT.platform())) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
// ignore
}
}
PagedResult<UserTO> users = USER_SERVICE.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).
getPlainAttr("aLong").get().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())) {
TASK_SERVICE.delete(TaskType.PULL, newTask.getKey());
}
RESOURCE_SERVICE.delete(newResource.getKey());
}
}
@Test
public void createDoubleMembership() {
AnyObjectCR anyObjectCR = AnyObjectITCase.getSample("createDoubleMembership");
anyObjectCR.setRealm("/even/two");
anyObjectCR.getMemberships().add(new MembershipTO.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build());
anyObjectCR.getMemberships().add(new MembershipTO.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build());
try {
createAnyObject(anyObjectCR);
fail("This should not happen");
} catch (SyncopeClientException e) {
assertEquals(ClientExceptionType.InvalidMembership, e.getType());
}
}
@Test
public void updateDoubleMembership() {
AnyObjectCR anyObjecCR = AnyObjectITCase.getSample("update");
anyObjecCR.setRealm("/even/two");
AnyObjectTO anyObjecTO = createAnyObject(anyObjecCR).getEntity();
assertNotNull(anyObjecTO.getKey());
AnyObjectUR req = new AnyObjectUR();
req.setKey(anyObjecTO.getKey());
req.getMemberships().add(new MembershipUR.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build());
MembershipUR mp = new MembershipUR.Builder("034740a9-fa10-453b-af37-dc7897e98fb1").build();
mp.getPlainAttrs().add(attr("any", "useless"));
req.getMemberships().add(mp);
try {
updateAnyObject(req).getEntity();
fail("This should not happen");
} catch (SyncopeClientException e) {
assertEquals(ClientExceptionType.InvalidMembership, e.getType());
}
}
}