| /* |
| * 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.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 com.fasterxml.jackson.databind.MappingIterator; |
| import com.fasterxml.jackson.dataformat.csv.CsvMapper; |
| import com.fasterxml.jackson.dataformat.csv.CsvSchema; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| import javax.ws.rs.core.HttpHeaders; |
| import javax.ws.rs.core.Response; |
| import org.apache.cxf.jaxrs.client.Client; |
| import org.apache.cxf.jaxrs.client.WebClient; |
| import org.apache.syncope.client.lib.SyncopeClient; |
| import org.apache.syncope.common.lib.Attr; |
| import org.apache.syncope.common.lib.SyncopeConstants; |
| import org.apache.syncope.common.lib.request.AnyObjectCR; |
| import org.apache.syncope.common.lib.to.AnyObjectTO; |
| import org.apache.syncope.common.lib.to.PagedResult; |
| import org.apache.syncope.common.lib.to.ProvisioningReport; |
| import org.apache.syncope.common.lib.to.PullTaskTO; |
| import org.apache.syncope.common.lib.to.PushTaskTO; |
| import org.apache.syncope.common.lib.to.ReconStatus; |
| import org.apache.syncope.common.lib.to.UserTO; |
| import org.apache.syncope.common.lib.types.AnyTypeKind; |
| import org.apache.syncope.common.lib.types.MatchType; |
| import org.apache.syncope.common.lib.types.ResourceOperation; |
| import org.apache.syncope.common.lib.types.UnmatchingRule; |
| import org.apache.syncope.common.rest.api.RESTHeaders; |
| import org.apache.syncope.common.rest.api.beans.AnyQuery; |
| import org.apache.syncope.common.rest.api.beans.CSVPullSpec; |
| import org.apache.syncope.common.rest.api.beans.CSVPushSpec; |
| import org.apache.syncope.common.rest.api.beans.ReconQuery; |
| import org.apache.syncope.common.rest.api.service.ReconciliationService; |
| import org.apache.syncope.fit.AbstractITCase; |
| import org.identityconnectors.framework.common.objects.OperationalAttributes; |
| import org.identityconnectors.framework.common.objects.Uid; |
| import org.junit.jupiter.api.Test; |
| import org.springframework.jdbc.core.JdbcTemplate; |
| |
| public class ReconciliationITCase extends AbstractITCase { |
| |
| @Test |
| public void push() { |
| // 1. create printer, with no resources |
| AnyObjectCR printerCR = AnyObjectITCase.getSample("reconciliation"); |
| printerCR.getResources().clear(); |
| AnyObjectTO printer = createAnyObject(printerCR).getEntity(); |
| assertNotNull(printer.getKey()); |
| |
| // 2. verify no printer with that name is on the external resource's db |
| JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); |
| assertEquals(0, jdbcTemplate.queryForList( |
| "SELECT id FROM testPRINTER WHERE printername=?", printer.getName()).size()); |
| |
| // 3. verify reconciliation status |
| ReconStatus status = RECONCILIATION_SERVICE.status( |
| new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).anyKey(printer.getName()).build()); |
| assertNotNull(status); |
| assertEquals(AnyTypeKind.ANY_OBJECT, status.getAnyTypeKind()); |
| assertEquals(printer.getKey(), status.getAnyKey()); |
| assertEquals(MatchType.ANY, status.getMatchType()); |
| assertNotNull(status.getOnSyncope()); |
| assertNull(status.getOnResource()); |
| |
| // 4. push |
| PushTaskTO pushTask = new PushTaskTO(); |
| pushTask.setPerformCreate(true); |
| pushTask.setUnmatchingRule(UnmatchingRule.PROVISION); |
| RECONCILIATION_SERVICE.push(new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED). |
| anyKey(printer.getKey()).build(), pushTask); |
| |
| // 5. verify that printer is now propagated |
| assertEquals(1, jdbcTemplate.queryForList( |
| "SELECT id FROM testPRINTER WHERE printername=?", printer.getName()).size()); |
| |
| // 6. verify resource was not assigned |
| printer = ANY_OBJECT_SERVICE.read(printer.getKey()); |
| assertTrue(printer.getResources().isEmpty()); |
| |
| // 7. verify reconciliation status |
| status = RECONCILIATION_SERVICE.status( |
| new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).anyKey(printer.getName()).build()); |
| assertNotNull(status); |
| assertNotNull(status.getOnSyncope()); |
| assertNotNull(status.getOnResource()); |
| |
| // __ENABLE__ management depends on the actual connector... |
| Attr enable = status.getOnSyncope().getAttr(OperationalAttributes.ENABLE_NAME).orElse(null); |
| if (enable != null) { |
| status.getOnSyncope().getAttrs().remove(enable); |
| } |
| // FIQL is always null for Syncope |
| assertNull(status.getOnSyncope().getFiql()); |
| assertNotNull(status.getOnResource().getFiql()); |
| status.getOnResource().setFiql(null); |
| assertEquals(status.getOnSyncope(), status.getOnResource()); |
| } |
| |
| @Test |
| public void pull() { |
| // 1. create printer, with no resources |
| AnyObjectCR printerCR = AnyObjectITCase.getSample("reconciliation"); |
| printerCR.getResources().clear(); |
| AnyObjectTO printer = createAnyObject(printerCR).getEntity(); |
| assertNotNull(printer.getKey()); |
| assertNotEquals("Nowhere", printer.getPlainAttr("location").get().getValues().get(0)); |
| |
| // 2. add row into the external resource's table, with same name |
| JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); |
| jdbcTemplate.update( |
| "INSERT INTO TESTPRINTER (id, printername, location, deleted, lastmodification) VALUES (?,?,?,?,?)", |
| printer.getKey(), printer.getName(), "Nowhere", false, new Date()); |
| |
| // 3. verify reconciliation status |
| ReconStatus status = RECONCILIATION_SERVICE.status( |
| new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).anyKey(printer.getName()).build()); |
| assertNotNull(status); |
| assertNotNull(status.getOnSyncope()); |
| assertNotNull(status.getOnResource()); |
| assertNotEquals(status.getOnSyncope().getAttr("LOCATION"), status.getOnResource().getAttr("LOCATION")); |
| |
| // 4. pull |
| PullTaskTO pullTask = new PullTaskTO(); |
| pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM); |
| pullTask.setPerformUpdate(true); |
| RECONCILIATION_SERVICE.pull(new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED). |
| anyKey(printer.getName()).build(), pullTask); |
| |
| // 5. verify reconciliation result (and resource is still not assigned) |
| printer = ANY_OBJECT_SERVICE.read(printer.getKey()); |
| assertEquals("Nowhere", printer.getPlainAttr("location").get().getValues().get(0)); |
| assertTrue(printer.getResources().isEmpty()); |
| } |
| |
| @Test |
| public void importSingle() { |
| // 1. add row into the external resource's table |
| String externalKey = UUID.randomUUID().toString(); |
| String externalName = "printer" + getUUIDString(); |
| |
| JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource); |
| jdbcTemplate.update( |
| "INSERT INTO TESTPRINTER (id, printername, location, deleted, lastmodification) VALUES (?,?,?,?,?)", |
| externalKey, externalName, "Nowhere", false, new Date()); |
| |
| // 2. verify reconciliation status |
| ReconStatus status = RECONCILIATION_SERVICE.status( |
| new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).fiql("ID==" + externalKey).build()); |
| assertNotNull(status); |
| assertNull(status.getAnyTypeKind()); |
| assertNull(status.getAnyKey()); |
| assertNull(status.getMatchType()); |
| assertNull(status.getOnSyncope()); |
| assertNotNull(status.getOnResource()); |
| assertEquals(externalKey, status.getOnResource().getAttr(Uid.NAME).get().getValues().get(0)); |
| assertEquals(externalName, status.getOnResource().getAttr("PRINTERNAME").get().getValues().get(0)); |
| |
| // 3. pull |
| PullTaskTO pullTask = new PullTaskTO(); |
| pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM); |
| pullTask.setPerformCreate(true); |
| RECONCILIATION_SERVICE.pull( |
| new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).fiql("ID==" + externalKey).build(), pullTask); |
| |
| // 4. verify reconciliation result |
| AnyObjectTO printer = ANY_OBJECT_SERVICE.read(externalName); |
| assertNotNull(printer); |
| } |
| |
| @Test |
| public void importCSV() { |
| ReconciliationService service = ADMIN_CLIENT.getService(ReconciliationService.class); |
| Client client = WebClient.client(service); |
| client.type(RESTHeaders.TEXT_CSV); |
| |
| CSVPullSpec spec = new CSVPullSpec.Builder(AnyTypeKind.USER.name(), "username").build(); |
| InputStream csv = getClass().getResourceAsStream("/test1.csv"); |
| |
| List<ProvisioningReport> results = service.pull(spec, csv); |
| assertEquals(AnyTypeKind.USER.name(), results.get(0).getAnyType()); |
| assertNotNull(results.get(0).getKey()); |
| assertEquals("donizetti", results.get(0).getName()); |
| assertEquals("donizetti", results.get(0).getUidValue()); |
| assertEquals(ResourceOperation.CREATE, results.get(0).getOperation()); |
| assertEquals(ProvisioningReport.Status.SUCCESS, results.get(0).getStatus()); |
| |
| UserTO donizetti = USER_SERVICE.read(results.get(0).getKey()); |
| assertNotNull(donizetti); |
| assertEquals("Gaetano", donizetti.getPlainAttr("firstname").get().getValues().get(0)); |
| assertEquals(1, donizetti.getPlainAttr("loginDate").get().getValues().size()); |
| |
| UserTO cimarosa = USER_SERVICE.read(results.get(1).getKey()); |
| assertNotNull(cimarosa); |
| assertEquals("Domenico Cimarosa", cimarosa.getPlainAttr("fullname").get().getValues().get(0)); |
| assertEquals(2, cimarosa.getPlainAttr("loginDate").get().getValues().size()); |
| } |
| |
| @Test |
| public void exportCSV() throws IOException { |
| ReconciliationService service = ADMIN_CLIENT.getService(ReconciliationService.class); |
| Client client = WebClient.client(service); |
| client.accept(RESTHeaders.TEXT_CSV); |
| |
| AnyQuery anyQuery = new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). |
| fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("*ini").query()). |
| page(1). |
| size(1000). |
| orderBy("username ASC"). |
| build(); |
| |
| CSVPushSpec spec = new CSVPushSpec.Builder(AnyTypeKind.USER.name()).ignorePaging(true). |
| field("username"). |
| field("status"). |
| plainAttr("firstname"). |
| plainAttr("surname"). |
| plainAttr("email"). |
| plainAttr("loginDate"). |
| build(); |
| |
| Response response = service.push(anyQuery, spec); |
| assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); |
| assertEquals( |
| "attachment; filename=" + SyncopeConstants.MASTER_DOMAIN + ".csv", |
| response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION)); |
| |
| PagedResult<UserTO> users = USER_SERVICE.search(anyQuery); |
| assertNotNull(users); |
| |
| MappingIterator<Map<String, String>> reader = new CsvMapper().readerFor(Map.class). |
| with(CsvSchema.emptySchema().withHeader()).readValues((InputStream) response.getEntity()); |
| |
| int rows = 0; |
| for (; reader.hasNext(); rows++) { |
| Map<String, String> row = reader.next(); |
| |
| assertEquals(users.getResult().get(rows).getUsername(), row.get("username")); |
| assertEquals(users.getResult().get(rows).getStatus(), row.get("status")); |
| |
| switch (row.get("username")) { |
| case "rossini": |
| assertEquals(spec.getNullValue(), row.get("email")); |
| assertTrue(row.get("loginDate").contains(spec.getArrayElementSeparator())); |
| break; |
| |
| case "verdi": |
| assertEquals("verdi@syncope.org", row.get("email")); |
| assertEquals(spec.getNullValue(), row.get("loginDate")); |
| break; |
| |
| case "bellini": |
| assertEquals(spec.getNullValue(), row.get("email")); |
| assertFalse(row.get("loginDate").contains(spec.getArrayElementSeparator())); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| assertEquals(rows, users.getTotalCount()); |
| } |
| } |