View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.fit.core;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.Optional;
29  import javax.ws.rs.core.GenericType;
30  import javax.ws.rs.core.Response;
31  import org.apache.commons.lang3.SerializationUtils;
32  import org.apache.syncope.common.lib.Attr;
33  import org.apache.syncope.common.lib.SyncopeClientException;
34  import org.apache.syncope.common.lib.request.GroupCR;
35  import org.apache.syncope.common.lib.request.PasswordPatch;
36  import org.apache.syncope.common.lib.request.StatusR;
37  import org.apache.syncope.common.lib.request.StringPatchItem;
38  import org.apache.syncope.common.lib.request.UserCR;
39  import org.apache.syncope.common.lib.request.UserUR;
40  import org.apache.syncope.common.lib.to.AnyTypeClassTO;
41  import org.apache.syncope.common.lib.to.ConnInstanceTO;
42  import org.apache.syncope.common.lib.to.ConnObject;
43  import org.apache.syncope.common.lib.to.GroupTO;
44  import org.apache.syncope.common.lib.to.Item;
45  import org.apache.syncope.common.lib.to.Mapping;
46  import org.apache.syncope.common.lib.to.MembershipTO;
47  import org.apache.syncope.common.lib.to.Provision;
48  import org.apache.syncope.common.lib.to.ProvisioningResult;
49  import org.apache.syncope.common.lib.to.ResourceTO;
50  import org.apache.syncope.common.lib.to.UserTO;
51  import org.apache.syncope.common.lib.to.VirSchemaTO;
52  import org.apache.syncope.common.lib.types.AnyTypeKind;
53  import org.apache.syncope.common.lib.types.ConnConfProperty;
54  import org.apache.syncope.common.lib.types.ExecStatus;
55  import org.apache.syncope.common.lib.types.MappingPurpose;
56  import org.apache.syncope.common.lib.types.PatchOperation;
57  import org.apache.syncope.common.lib.types.SchemaType;
58  import org.apache.syncope.common.lib.types.StatusRType;
59  import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
60  import org.apache.syncope.common.rest.api.service.ResourceService;
61  import org.apache.syncope.fit.AbstractITCase;
62  import org.identityconnectors.framework.common.objects.ObjectClass;
63  import org.junit.jupiter.api.Test;
64  import org.springframework.jdbc.core.JdbcTemplate;
65  
66  public class VirAttrITCase extends AbstractITCase {
67  
68      @Test
69      public void issueSYNCOPE16() {
70          UserCR userCR = UserITCase.getUniqueSample("issue16@apache.org");
71          userCR.getVirAttrs().add(attr("virtualdata", "virtualvalue"));
72          userCR.getResources().add(RESOURCE_NAME_DBVIRATTR);
73          userCR.getMemberships().add(new MembershipTO.Builder("f779c0d4-633b-4be5-8f57-32eb478a3ca5").build());
74  
75          // 1. create user
76          UserTO userTO = createUser(userCR).getEntity();
77          assertNotNull(userTO);
78  
79          // 2. check for virtual attribute value
80          userTO = USER_SERVICE.read(userTO.getKey());
81          assertNotNull(userTO);
82          assertEquals("virtualvalue", userTO.getVirAttr("virtualdata").get().getValues().get(0));
83  
84          UserUR userUR = new UserUR();
85          userUR.setKey(userTO.getKey());
86          userUR.getVirAttrs().add(attr("virtualdata", "virtualupdated"));
87  
88          // 3. update virtual attribute
89          userTO = updateUser(userUR).getEntity();
90          assertNotNull(userTO);
91  
92          // 4. check for virtual attribute value
93          userTO = USER_SERVICE.read(userTO.getKey());
94          assertNotNull(userTO);
95          assertEquals("virtualupdated", userTO.getVirAttr("virtualdata").get().getValues().get(0));
96      }
97  
98      @Test
99      public void issueSYNCOPE260() {
100         // create new virtual schema for the resource below
101         ResourceTO ws2 = RESOURCE_SERVICE.read(RESOURCE_NAME_WS2);
102         Provision provision = ws2.getProvision(AnyTypeKind.USER.name()).get();
103         assertNotNull(provision);
104 
105         VirSchemaTO virSchema = new VirSchemaTO();
106         virSchema.setKey("syncope260" + getUUIDString());
107         virSchema.setExtAttrName("companyName");
108         virSchema.setResource(RESOURCE_NAME_WS2);
109         virSchema.setAnyType(provision.getAnyType());
110         virSchema = createSchema(SchemaType.VIRTUAL, virSchema);
111         assertNotNull(virSchema);
112 
113         AnyTypeClassTO newClass = new AnyTypeClassTO();
114         newClass.setKey("syncope260" + getUUIDString());
115         newClass.getVirSchemas().add(virSchema.getKey());
116         Response response = ANY_TYPE_CLASS_SERVICE.create(newClass);
117         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
118         newClass = getObject(response.getLocation(), AnyTypeClassService.class, AnyTypeClassTO.class);
119 
120         // ----------------------------------
121         // create user and check virtual attribute value propagation
122         // ----------------------------------
123         UserCR userCR = UserITCase.getUniqueSample("260@a.com");
124         userCR.getAuxClasses().add(newClass.getKey());
125         userCR.getVirAttrs().add(attr(virSchema.getKey(), "virtualvalue"));
126         userCR.getResources().add(RESOURCE_NAME_WS2);
127 
128         ProvisioningResult<UserTO> result = createUser(userCR);
129         assertNotNull(result);
130         assertFalse(result.getPropagationStatuses().isEmpty());
131         assertEquals(RESOURCE_NAME_WS2, result.getPropagationStatuses().get(0).getResource());
132         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
133         UserTO userTO = result.getEntity();
134 
135         ConnObject connObjectTO =
136                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
137         assertEquals("virtualvalue", connObjectTO.getAttr("COMPANYNAME").get().getValues().get(0));
138         // ----------------------------------
139 
140         // ----------------------------------
141         // update user virtual attribute and check virtual attribute value update propagation
142         // ----------------------------------
143         UserUR userUR = new UserUR();
144         userUR.setKey(userTO.getKey());
145         userUR.getVirAttrs().add(attr(virSchema.getKey(), "virtualvalue2"));
146 
147         result = updateUser(userUR);
148         assertNotNull(result);
149         assertFalse(result.getPropagationStatuses().isEmpty());
150         assertEquals(RESOURCE_NAME_WS2, result.getPropagationStatuses().get(0).getResource());
151         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
152         userTO = result.getEntity();
153 
154         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
155         assertEquals("virtualvalue2", connObjectTO.getAttr("COMPANYNAME").get().getValues().get(0));
156         // ----------------------------------
157 
158         // ----------------------------------
159         // suspend/reactivate user and check virtual attribute value (unchanged)
160         // ----------------------------------
161         StatusR statusR = new StatusR.Builder(userTO.getKey(), StatusRType.SUSPEND).build();
162         userTO = USER_SERVICE.status(statusR).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
163         }).getEntity();
164         assertEquals("suspended", userTO.getStatus());
165 
166         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
167         assertEquals("virtualvalue2", connObjectTO.getAttr("COMPANYNAME").get().getValues().get(0));
168 
169         statusR = new StatusR.Builder(userTO.getKey(), StatusRType.REACTIVATE).build();
170         userTO = USER_SERVICE.status(statusR).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
171         }).getEntity();
172         assertEquals("active", userTO.getStatus());
173 
174         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
175         assertEquals("virtualvalue2", connObjectTO.getAttr("COMPANYNAME").get().getValues().get(0));
176         // ----------------------------------
177 
178         // ----------------------------------
179         // update user attribute and check virtual attribute value (unchanged)
180         // ----------------------------------
181         userUR = new UserUR();
182         userUR.setKey(userTO.getKey());
183         userUR.getPlainAttrs().add(attrAddReplacePatch("surname", "Surname2"));
184 
185         result = updateUser(userUR);
186         assertNotNull(result);
187         assertFalse(result.getPropagationStatuses().isEmpty());
188         assertEquals(RESOURCE_NAME_WS2, result.getPropagationStatuses().get(0).getResource());
189         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
190         userTO = result.getEntity();
191 
192         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
193         assertEquals("Surname2", connObjectTO.getAttr("SURNAME").get().getValues().get(0));
194 
195         // virtual attribute value did not change
196         assertFalse(connObjectTO.getAttr("COMPANYNAME").get().getValues().isEmpty());
197         assertEquals("virtualvalue2", connObjectTO.getAttr("COMPANYNAME").get().getValues().get(0));
198         // ----------------------------------
199     }
200 
201     @Test
202     public void virAttrCache() {
203         UserCR userCR = UserITCase.getUniqueSample("virattrcache@apache.org");
204         userCR.getVirAttrs().clear();
205 
206         Attr virAttr = new Attr();
207         virAttr.setSchema("virtualdata");
208         virAttr.getValues().add("virattrcache");
209         userCR.getVirAttrs().add(virAttr);
210 
211         userCR.getMemberships().clear();
212         userCR.getResources().clear();
213         userCR.getResources().add(RESOURCE_NAME_DBVIRATTR);
214 
215         // 1. create user
216         UserTO actual = createUser(userCR).getEntity();
217         assertNotNull(actual);
218 
219         // 2. check for virtual attribute value
220         actual = USER_SERVICE.read(actual.getKey());
221         assertEquals("virattrcache", actual.getVirAttr("virtualdata").get().getValues().get(0));
222 
223         // 3. update virtual attribute directly
224         JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
225         String value = queryForObject(jdbcTemplate,
226                 MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, actual.getKey());
227         assertEquals("virattrcache", value);
228 
229         jdbcTemplate.update("UPDATE testpull set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
230 
231         value = queryForObject(jdbcTemplate,
232                 MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, actual.getKey());
233         assertEquals("virattrcache2", value);
234 
235         // 4. check for cached attribute value
236         actual = USER_SERVICE.read(actual.getKey());
237         assertEquals("virattrcache", actual.getVirAttr("virtualdata").get().getValues().get(0));
238 
239         UserUR userUR = new UserUR();
240         userUR.setKey(actual.getKey());
241         userUR.getVirAttrs().add(attr("virtualdata", "virtualupdated"));
242 
243         // 5. update virtual attribute
244         actual = updateUser(userUR).getEntity();
245         assertNotNull(actual);
246 
247         // 6. check for virtual attribute value
248         actual = USER_SERVICE.read(actual.getKey());
249         assertNotNull(actual);
250         assertEquals("virtualupdated", actual.getVirAttr("virtualdata").get().getValues().get(0));
251     }
252 
253     @Test
254     public void issueSYNCOPE397() {
255         ResourceTO csv = RESOURCE_SERVICE.read(RESOURCE_NAME_CSV);
256 
257         // change mapping of resource-csv
258         Mapping origMapping = SerializationUtils.clone(csv.getProvisions().get(0).getMapping());
259         try {
260             // remove this mapping
261             Optional<Item> email = csv.getProvisions().get(0).getMapping().getItems().stream().
262                     filter(item -> "email".equals(item.getIntAttrName())).findFirst();
263             if (email.isPresent()) {
264                 csv.getProvisions().get(0).getMapping().getItems().remove(email.get());
265             }
266 
267             RESOURCE_SERVICE.update(csv);
268             csv = RESOURCE_SERVICE.read(RESOURCE_NAME_CSV);
269             assertNotNull(csv.getProvisions().get(0).getMapping());
270 
271             // create new virtual schema for the resource below
272             Provision provision = csv.getProvision(AnyTypeKind.USER.name()).get();
273             assertNotNull(provision);
274 
275             VirSchemaTO virSchema = new VirSchemaTO();
276             virSchema.setKey("syncope397" + getUUIDString());
277             virSchema.setExtAttrName("email");
278             virSchema.setResource(RESOURCE_NAME_CSV);
279             virSchema.setAnyType(provision.getAnyType());
280             virSchema = createSchema(SchemaType.VIRTUAL, virSchema);
281             assertNotNull(virSchema);
282 
283             AnyTypeClassTO newClass = new AnyTypeClassTO();
284             newClass.setKey("syncope397" + getUUIDString());
285             newClass.getVirSchemas().add(virSchema.getKey());
286             Response response = ANY_TYPE_CLASS_SERVICE.create(newClass);
287             assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
288             newClass = getObject(response.getLocation(), AnyTypeClassService.class, AnyTypeClassTO.class);
289 
290             // create a new user
291             UserCR userCR = UserITCase.getUniqueSample("397@syncope.apache.org");
292             userCR.getAuxClasses().add("csv");
293             userCR.getAuxClasses().add(newClass.getKey());
294             userCR.getResources().clear();
295             userCR.getMemberships().clear();
296 
297             userCR.getVirAttrs().clear();
298             userCR.getVirAttrs().add(attr(virSchema.getKey(), "test@testone.org"));
299             // assign resource-csv to user
300             userCR.getResources().add(RESOURCE_NAME_CSV);
301             // save user
302             UserTO userTO = createUser(userCR).getEntity();
303             // make std controls about user
304             assertNotNull(userTO);
305             assertTrue(RESOURCE_NAME_CSV.equals(userTO.getResources().iterator().next()));
306             assertEquals("test@testone.org", userTO.getVirAttrs().iterator().next().getValues().get(0));
307 
308             // update user
309             UserTO toBeUpdated = USER_SERVICE.read(userTO.getKey());
310             UserUR userUR = new UserUR();
311             userUR.setKey(toBeUpdated.getKey());
312             userUR.setPassword(new PasswordPatch.Builder().value("password234").build());
313             // assign new resource to user
314             userUR.getResources().add(new StringPatchItem.Builder().
315                     operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS2).build());
316             // modify virtual attribute
317             userUR.getVirAttrs().add(attr(virSchema.getKey(), "test@testoneone.com"));
318 
319             // check Syncope change password
320             userUR.setPassword(new PasswordPatch.Builder().
321                     value("password234").
322                     onSyncope(true).
323                     resource(RESOURCE_NAME_WS2).
324                     build());
325 
326             ProvisioningResult<UserTO> result = updateUser(userUR);
327             assertNotNull(result);
328             toBeUpdated = result.getEntity();
329             assertTrue(toBeUpdated.getVirAttrs().iterator().next().getValues().contains("test@testoneone.com"));
330             // check if propagates correctly with assertEquals on size of tasks list
331             assertEquals(2, result.getPropagationStatuses().size());
332         } finally {
333             // restore mapping of resource-csv
334             csv.getProvisions().get(0).setMapping(origMapping);
335             RESOURCE_SERVICE.update(csv);
336         }
337     }
338 
339     @Test
340     public void issueSYNCOPE442() {
341         UserCR userCR = UserITCase.getUniqueSample("syncope442@apache.org");
342         userCR.getVirAttrs().clear();
343 
344         Attr virAttr = new Attr();
345         virAttr.setSchema("virtualdata");
346         virAttr.getValues().add("virattrcache");
347         userCR.getVirAttrs().add(virAttr);
348 
349         userCR.getMemberships().clear();
350         userCR.getResources().clear();
351         userCR.getResources().add(RESOURCE_NAME_DBVIRATTR);
352 
353         // 1. create user
354         UserTO userTO = createUser(userCR).getEntity();
355         assertNotNull(userTO);
356 
357         // 2. check for virtual attribute value
358         userTO = USER_SERVICE.read(userTO.getKey());
359         assertEquals("virattrcache", userTO.getVirAttr("virtualdata").get().getValues().get(0));
360 
361         // ----------------------------------------
362         // 3. change connector URL so that we are sure that any provided value will come from virtual cache
363         // ----------------------------------------
364         String jdbcURL = null;
365         ConnInstanceTO connInstanceTO = CONNECTOR_SERVICE.readByResource(
366                 RESOURCE_NAME_DBVIRATTR, Locale.ENGLISH.getLanguage());
367         for (ConnConfProperty prop : connInstanceTO.getConf()) {
368             if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
369                 jdbcURL = prop.getValues().iterator().next().toString();
370                 prop.getValues().clear();
371                 prop.getValues().add("jdbc:h2:tcp://localhost:9092/xxx");
372             }
373         }
374 
375         CONNECTOR_SERVICE.update(connInstanceTO);
376         // ----------------------------------------
377 
378         // ----------------------------------------
379         // 4. update value on external resource
380         // ----------------------------------------
381         JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
382         String value = queryForObject(jdbcTemplate,
383                 MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.
384                         getKey());
385         assertEquals("virattrcache", value);
386 
387         jdbcTemplate.update("UPDATE testpull set USERNAME='virattrcache2' WHERE ID=?", userTO.getKey());
388 
389         value = queryForObject(jdbcTemplate,
390                 MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.getKey());
391         assertEquals("virattrcache2", value);
392         // ----------------------------------------
393 
394         userTO = USER_SERVICE.read(userTO.getKey());
395         assertEquals("virattrcache", userTO.getVirAttr("virtualdata").get().getValues().get(0));
396 
397         // ----------------------------------------
398         // 5. restore connector URL, values can be read again from external resource
399         // ----------------------------------------
400         for (ConnConfProperty prop : connInstanceTO.getConf()) {
401             if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
402                 prop.getValues().clear();
403                 prop.getValues().add(jdbcURL);
404             }
405         }
406 
407         CONNECTOR_SERVICE.update(connInstanceTO);
408         // ----------------------------------------
409 
410         // cached value still in place...
411         userTO = USER_SERVICE.read(userTO.getKey());
412         assertEquals("virattrcache", userTO.getVirAttr("virtualdata").get().getValues().get(0));
413 
414         // force cache update by adding a resource which has virtualdata mapped for propagation
415         UserUR userUR = new UserUR();
416         userUR.setKey(userTO.getKey());
417         userUR.getResources().add(new StringPatchItem.Builder().
418                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS2).build());
419         userTO = updateUser(userUR).getEntity();
420         assertNotNull(userTO);
421 
422         userTO = USER_SERVICE.read(userTO.getKey());
423         assertEquals("virattrcache2", userTO.getVirAttr("virtualdata").get().getValues().get(0));
424     }
425 
426     @Test
427     public void issueSYNCOPE436() {
428         UserCR userCR = UserITCase.getUniqueSample("syncope436@syncope.apache.org");
429         userCR.getMemberships().clear();
430         userCR.getResources().clear();
431         userCR.getResources().add(RESOURCE_NAME_LDAP);
432         userCR.getVirAttrs().add(attr("virtualReadOnly", "readOnly"));
433         UserTO userTO = createUser(userCR).getEntity();
434         // finding no values because the virtual attribute is readonly 
435         assertTrue(userTO.getVirAttr("virtualReadOnly").get().getValues().isEmpty());
436     }
437 
438     @Test
439     public void issueSYNCOPE453() {
440         String resourceName = "issueSYNCOPE453Res" + getUUIDString();
441         String groupKey = null;
442         String groupName = "issueSYNCOPE453Group" + getUUIDString();
443 
444         try {
445             // -------------------------------------------
446             // Create a VirAttrITCase ad-hoc
447             // -------------------------------------------
448             VirSchemaTO rvirtualdata;
449             try {
450                 rvirtualdata = SCHEMA_SERVICE.read(SchemaType.VIRTUAL, "rvirtualdata");
451             } catch (SyncopeClientException e) {
452                 LOG.warn("rvirtualdata not found, re-creating", e);
453 
454                 rvirtualdata = new VirSchemaTO();
455                 rvirtualdata.setKey("rvirtualdata");
456                 rvirtualdata.setExtAttrName("businessCategory");
457                 rvirtualdata.setResource(RESOURCE_NAME_LDAP);
458                 rvirtualdata.setAnyType(AnyTypeKind.GROUP.name());
459 
460                 rvirtualdata = createSchema(SchemaType.VIRTUAL, rvirtualdata);
461             }
462             assertNotNull(rvirtualdata);
463 
464             if (!"minimal group".equals(rvirtualdata.getAnyTypeClass())) {
465                 LOG.warn("rvirtualdata not in minimal group, restoring");
466 
467                 AnyTypeClassTO minimalGroup = ANY_TYPE_CLASS_SERVICE.read("minimal group");
468                 minimalGroup.getVirSchemas().add(rvirtualdata.getKey());
469                 ANY_TYPE_CLASS_SERVICE.update(minimalGroup);
470 
471                 rvirtualdata = SCHEMA_SERVICE.read(SchemaType.VIRTUAL, rvirtualdata.getKey());
472                 assertEquals("minimal group", rvirtualdata.getAnyTypeClass());
473             }
474 
475             // -------------------------------------------
476             // Create a resource ad-hoc
477             // -------------------------------------------
478             ResourceTO resourceTO = new ResourceTO();
479 
480             resourceTO.setKey(resourceName);
481             resourceTO.setConnector("be24b061-019d-4e3e-baf0-0a6d0a45cb9c");
482 
483             Provision provisionTO = new Provision();
484             provisionTO.setAnyType(AnyTypeKind.USER.name());
485             provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME);
486             resourceTO.getProvisions().add(provisionTO);
487 
488             Mapping mapping = new Mapping();
489             provisionTO.setMapping(mapping);
490 
491             Item item = new Item();
492             item.setIntAttrName("fullname");
493             item.setExtAttrName("ID");
494             item.setPurpose(MappingPurpose.PROPAGATION);
495             item.setConnObjectKey(true);
496             mapping.setConnObjectKeyItem(item);
497 
498             item = new Item();
499             item.setIntAttrName("username");
500             item.setExtAttrName("USERNAME");
501             item.setPurpose(MappingPurpose.PROPAGATION);
502             mapping.getItems().add(item);
503 
504             item = new Item();
505             item.setIntAttrName("groups[" + groupName + "].rvirtualdata");
506             item.setExtAttrName("EMAIL");
507             item.setPurpose(MappingPurpose.PROPAGATION);
508             mapping.getItems().add(item);
509 
510             assertNotNull(getObject(
511                     RESOURCE_SERVICE.create(resourceTO).getLocation(), ResourceService.class, ResourceTO.class));
512             // -------------------------------------------
513 
514             GroupCR groupCR = new GroupCR();
515             groupCR.setName(groupName);
516             groupCR.setRealm("/");
517             groupCR.getVirAttrs().add(attr(rvirtualdata.getKey(), "ml@group.it"));
518             groupCR.getResources().add(RESOURCE_NAME_LDAP);
519             GroupTO groupTO = createGroup(groupCR).getEntity();
520             groupKey = groupTO.getKey();
521             assertEquals(1, groupTO.getVirAttrs().size());
522             assertEquals("ml@group.it", groupTO.getVirAttrs().iterator().next().getValues().get(0));
523             // -------------------------------------------
524 
525             // -------------------------------------------
526             // Create new user
527             // -------------------------------------------
528             UserCR userCR = UserITCase.getUniqueSample("syn453@syncope.apache.org");
529             userCR.getPlainAttrs().add(attr("fullname", "123"));
530             userCR.getResources().clear();
531             userCR.getResources().add(resourceName);
532             userCR.getVirAttrs().clear();
533             userCR.getMemberships().clear();
534 
535             userCR.getMemberships().add(new MembershipTO.Builder(groupTO.getKey()).build());
536 
537             ProvisioningResult<UserTO> result = createUser(userCR);
538             assertEquals(2, result.getPropagationStatuses().size());
539             assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
540             assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(1).getStatus());
541             UserTO userTO = result.getEntity();
542 
543             JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
544 
545             Map<String, Object> actuals = jdbcTemplate.queryForMap(
546                     "SELECT id, surname, email FROM testpull WHERE id=?",
547                     new Object[] { userTO.getPlainAttr("fullname").get().getValues().get(0) });
548 
549             assertEquals(userTO.getPlainAttr("fullname").get().getValues().get(0), actuals.get("id").toString());
550             assertEquals("ml@group.it", actuals.get("email"));
551             // -------------------------------------------
552         } catch (Exception e) {
553             LOG.error("Unexpected error", e);
554         } finally {
555             // -------------------------------------------
556             // Delete resource and group ad-hoc
557             // -------------------------------------------
558             RESOURCE_SERVICE.delete(resourceName);
559             if (groupKey != null) {
560                 GROUP_SERVICE.delete(groupKey);
561             }
562             // -------------------------------------------
563         }
564     }
565 
566     @Test
567     public void issueSYNCOPE459() {
568         UserCR userCR = UserITCase.getUniqueSample("syncope459@apache.org");
569         userCR.getResources().clear();
570         userCR.getResources().add(RESOURCE_NAME_LDAP);
571         userCR.getMemberships().clear();
572         userCR.getVirAttrs().clear();
573 
574         UserTO userTO = createUser(userCR).getEntity();
575 
576         assertNotNull(userTO.getVirAttr("virtualReadOnly"));
577     }
578 
579     @Test
580     public void issueSYNCOPE501() {
581         // 1. create user and propagate him on resource-db-virattr
582         UserCR userCR = UserITCase.getUniqueSample("syncope501@apache.org");
583         userCR.getResources().clear();
584         userCR.getMemberships().clear();
585         userCR.getVirAttrs().clear();
586 
587         userCR.getResources().add(RESOURCE_NAME_DBVIRATTR);
588 
589         // virtualdata is mapped with username
590         userCR.getVirAttrs().add(attr("virtualdata", "syncope501@apache.org"));
591 
592         UserTO userTO = createUser(userCR).getEntity();
593 
594         assertNotNull(userTO.getVirAttr("virtualdata"));
595         assertEquals("syncope501@apache.org", userTO.getVirAttr("virtualdata").get().getValues().get(0));
596 
597         // 2. update virtual attribute
598         UserUR userUR = new UserUR();
599         userUR.setKey(userTO.getKey());
600         // change virtual attribute value
601         userUR.getVirAttrs().add(attr("virtualdata", "syncope501_updated@apache.org"));
602 
603         userTO = updateUser(userUR).getEntity();
604         assertNotNull(userTO);
605 
606         // 3. check that user virtual attribute has really been updated 
607         assertFalse(userTO.getVirAttr("virtualdata").get().getValues().isEmpty());
608         assertEquals("syncope501_updated@apache.org", userTO.getVirAttr("virtualdata").get().getValues().get(0));
609     }
610 
611     @Test
612     public void issueSYNCOPE691() {
613         ResourceTO ldap = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP);
614         try {
615             Provision provision = ldap.getProvision(AnyTypeKind.USER.name()).orElse(null);
616             assertNotNull(provision);
617             provision.getMapping().getItems().removeIf(item -> "mail".equals(item.getExtAttrName()));
618             provision.getVirSchemas().clear();
619 
620             ldap.getProvisions().clear();
621             ldap.getProvisions().add(provision);
622             ldap.setKey(RESOURCE_NAME_LDAP + "691" + getUUIDString());
623             RESOURCE_SERVICE.create(ldap);
624 
625             ldap = RESOURCE_SERVICE.read(ldap.getKey());
626             provision = ldap.getProvision(AnyTypeKind.USER.name()).get();
627             assertNotNull(provision);
628 
629             // create new virtual schema for the resource below
630             VirSchemaTO virSchema = new VirSchemaTO();
631             virSchema.setKey("syncope691" + getUUIDString());
632             virSchema.setExtAttrName("mail");
633             virSchema.setResource(ldap.getKey());
634             virSchema.setAnyType(provision.getAnyType());
635             virSchema = createSchema(SchemaType.VIRTUAL, virSchema);
636             assertNotNull(virSchema);
637 
638             AnyTypeClassTO newClass = new AnyTypeClassTO();
639             newClass.setKey("syncope691" + getUUIDString());
640             newClass.getVirSchemas().add(virSchema.getKey());
641             Response response = ANY_TYPE_CLASS_SERVICE.create(newClass);
642             assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
643             newClass = getObject(response.getLocation(), AnyTypeClassService.class, AnyTypeClassTO.class);
644 
645             // create a new user
646             UserCR userCR = UserITCase.getUniqueSample("syncope691@syncope.apache.org");
647             userCR.getAuxClasses().add(newClass.getKey());
648             userCR.getResources().clear();
649             userCR.getMemberships().clear();
650             userCR.getVirAttrs().clear();
651 
652             Attr emailTO = new Attr();
653             emailTO.setSchema(virSchema.getKey());
654             emailTO.getValues().add("test@issue691.dom1.org");
655             emailTO.getValues().add("test@issue691.dom2.org");
656 
657             userCR.getVirAttrs().add(emailTO);
658             // assign resource-ldap691 to user
659             userCR.getResources().add(ldap.getKey());
660             // save user
661             UserTO userTO = createUser(userCR).getEntity();
662             // make std controls about user
663             assertNotNull(userTO);
664             assertTrue(ldap.getKey().equals(userTO.getResources().iterator().next()));
665 
666             assertEquals(2, userTO.getVirAttrs().iterator().next().getValues().size());
667             assertTrue(userTO.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom1.org"));
668             assertTrue(userTO.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom2.org"));
669 
670             // update user
671             UserUR userUR = new UserUR();
672             userUR.setKey(userTO.getKey());
673             // modify virtual attribute
674             userUR.getVirAttrs().add(new Attr.Builder(virSchema.getKey()).
675                     value("test@issue691.dom3.org").
676                     value("test@issue691.dom4.org").
677                     build());
678 
679             UserTO updated = updateUser(userUR).getEntity();
680             assertNotNull(updated);
681             assertEquals(2, updated.getVirAttrs().iterator().next().getValues().size());
682             assertTrue(updated.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom3.org"));
683             assertTrue(updated.getVirAttrs().iterator().next().getValues().contains("test@issue691.dom4.org"));
684         } finally {
685             try {
686                 RESOURCE_SERVICE.delete(ldap.getKey());
687             } catch (Exception ignore) {
688                 // ignore
689             }
690         }
691     }
692 }