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.assertNotEquals;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertNull;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import com.fasterxml.jackson.databind.node.ObjectNode;
29  import java.util.List;
30  import java.util.Optional;
31  import java.util.UUID;
32  import javax.naming.NamingException;
33  import javax.ws.rs.core.HttpHeaders;
34  import javax.ws.rs.core.MediaType;
35  import javax.ws.rs.core.Response;
36  import org.apache.commons.lang3.RandomStringUtils;
37  import org.apache.commons.lang3.StringUtils;
38  import org.apache.cxf.jaxrs.client.WebClient;
39  import org.apache.syncope.common.lib.SyncopeClientException;
40  import org.apache.syncope.common.lib.SyncopeConstants;
41  import org.apache.syncope.common.lib.policy.PullPolicyTO;
42  import org.apache.syncope.common.lib.request.LinkedAccountUR;
43  import org.apache.syncope.common.lib.request.UserCR;
44  import org.apache.syncope.common.lib.request.UserUR;
45  import org.apache.syncope.common.lib.to.ConnObject;
46  import org.apache.syncope.common.lib.to.ExecTO;
47  import org.apache.syncope.common.lib.to.ImplementationTO;
48  import org.apache.syncope.common.lib.to.LinkedAccountTO;
49  import org.apache.syncope.common.lib.to.PagedResult;
50  import org.apache.syncope.common.lib.to.PropagationTaskTO;
51  import org.apache.syncope.common.lib.to.PullTaskTO;
52  import org.apache.syncope.common.lib.to.PushTaskTO;
53  import org.apache.syncope.common.lib.to.ResourceTO;
54  import org.apache.syncope.common.lib.to.TaskTO;
55  import org.apache.syncope.common.lib.to.UserTO;
56  import org.apache.syncope.common.lib.types.AnyTypeKind;
57  import org.apache.syncope.common.lib.types.ExecStatus;
58  import org.apache.syncope.common.lib.types.IdMImplementationType;
59  import org.apache.syncope.common.lib.types.ImplementationEngine;
60  import org.apache.syncope.common.lib.types.MatchingRule;
61  import org.apache.syncope.common.lib.types.PatchOperation;
62  import org.apache.syncope.common.lib.types.PolicyType;
63  import org.apache.syncope.common.lib.types.PullMode;
64  import org.apache.syncope.common.lib.types.ResourceOperation;
65  import org.apache.syncope.common.lib.types.TaskType;
66  import org.apache.syncope.common.lib.types.UnmatchingRule;
67  import org.apache.syncope.common.rest.api.RESTHeaders;
68  import org.apache.syncope.common.rest.api.beans.TaskQuery;
69  import org.apache.syncope.common.rest.api.service.TaskService;
70  import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
71  import org.apache.syncope.fit.AbstractITCase;
72  import org.apache.syncope.fit.core.reference.LinkedAccountSamplePullCorrelationRule;
73  import org.apache.syncope.fit.core.reference.LinkedAccountSamplePullCorrelationRuleConf;
74  import org.junit.jupiter.api.Test;
75  
76  public class LinkedAccountITCase extends AbstractITCase {
77  
78      @Test
79      public void createWithLinkedAccountThenUpdateThenRemove() throws NamingException {
80          // 1. create user with linked account
81          UserCR userCR = UserITCase.getSample(
82                  "linkedAccount" + RandomStringUtils.randomNumeric(5) + "@syncope.apache.org");
83          String connObjectKeyValue = "firstAccountOf" + userCR.getUsername();
84          String privilege = APPLICATION_SERVICE.read("mightyApp").getPrivileges().get(0).getKey();
85  
86          LinkedAccountTO account = new LinkedAccountTO.Builder(RESOURCE_NAME_LDAP, connObjectKeyValue).build();
87          account.getPlainAttrs().add(attr("surname", "LINKED_SURNAME"));
88          account.getPrivileges().add(privilege);
89          userCR.getLinkedAccounts().add(account);
90  
91          UserTO user = createUser(userCR).getEntity();
92          assertNotNull(user.getKey());
93          assertEquals(privilege, user.getLinkedAccounts().get(0).getPrivileges().iterator().next());
94  
95          // 2. verify that propagation task was generated and that account is found on resource
96          PagedResult<PropagationTaskTO> tasks = TASK_SERVICE.search(
97                  new TaskQuery.Builder(TaskType.PROPAGATION).resource(RESOURCE_NAME_LDAP).
98                          anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
99          assertEquals(1, tasks.getTotalCount());
100         assertEquals(connObjectKeyValue, tasks.getResult().get(0).getConnObjectKey());
101         assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
102         assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
103 
104         ConnObject ldapObj = RESOURCE_SERVICE.readConnObject(
105                 RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), connObjectKeyValue);
106         assertNotNull(ldapObj);
107         assertEquals(user.getPlainAttr("email").get().getValues(), ldapObj.getAttr("mail").get().getValues());
108         assertEquals("LINKED_SURNAME", ldapObj.getAttr("sn").get().getValues().get(0));
109 
110         // 3. update linked account
111         UserUR userUR = new UserUR();
112         userUR.setKey(user.getKey());
113 
114         account.getPlainAttrs().clear();
115         account.getPlainAttrs().add(attr("email", "UPDATED_EMAIL@syncope.apache.org"));
116         account.getPlainAttrs().add(attr("surname", "UPDATED_SURNAME"));
117         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(account).build());
118 
119         user = updateUser(userUR).getEntity();
120         assertEquals(1, user.getLinkedAccounts().size());
121 
122         // 4 verify that account was updated on resource
123         ldapObj = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), connObjectKeyValue);
124         assertNotNull(ldapObj);
125 
126         assertTrue(ldapObj.getAttr("mail").get().getValues().contains("UPDATED_EMAIL@syncope.apache.org"));
127         assertEquals("UPDATED_SURNAME", ldapObj.getAttr("sn").get().getValues().get(0));
128 
129         // 5. remove linked account from user
130         userUR = new UserUR();
131         userUR.setKey(user.getKey());
132         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().
133                 operation(PatchOperation.DELETE).
134                 linkedAccountTO(user.getLinkedAccounts().get(0)).build());
135 
136         user = updateUser(userUR).getEntity();
137         assertTrue(user.getLinkedAccounts().isEmpty());
138 
139         // 6. verify that propagation task was generated and that account is not any more on resource
140         tasks = TASK_SERVICE.search(
141                 new TaskQuery.Builder(TaskType.PROPAGATION).resource(RESOURCE_NAME_LDAP).
142                         anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
143         assertEquals(3, tasks.getTotalCount());
144 
145         Optional<PropagationTaskTO> deletTask =
146                 tasks.getResult().stream().filter(task -> task.getOperation() == ResourceOperation.DELETE).findFirst();
147         assertTrue(deletTask.isPresent());
148         assertEquals(connObjectKeyValue, deletTask.get().getConnObjectKey());
149         assertEquals(ExecStatus.SUCCESS.name(), deletTask.get().getLatestExecStatus());
150 
151         assertNull(getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, connObjectKeyValue));
152     }
153 
154     @Test
155     public void createWithLinkedAccountThenUpdateUsingPutThenRemove() throws NamingException {
156         // 1. create user with linked account
157         UserCR userCR = UserITCase.getSample(
158                 "linkedAccount" + RandomStringUtils.randomNumeric(5) + "@syncope.apache.org");
159         String connObjectKeyValue = "uid=" + userCR.getUsername() + ",ou=People,o=isp";
160         String privilege = APPLICATION_SERVICE.read("mightyApp").getPrivileges().get(0).getKey();
161 
162         LinkedAccountTO account = new LinkedAccountTO.Builder(RESOURCE_NAME_LDAP, connObjectKeyValue).build();
163         account.setUsername("LinkedUsername");
164         account.getPlainAttrs().add(attr("surname", "LINKED_SURNAME"));
165         account.getPrivileges().add(privilege);
166         userCR.getLinkedAccounts().add(account);
167 
168         UserTO user = createUser(userCR).getEntity();
169         assertNotNull(user.getKey());
170         assertEquals(1, user.getLinkedAccounts().size());
171         assertEquals(privilege, user.getLinkedAccounts().get(0).getPrivileges().iterator().next());
172         assertEquals("LinkedUsername", user.getLinkedAccounts().get(0).getUsername());
173         assertEquals("LINKED_SURNAME", account.getPlainAttr("surname").get().getValues().get(0));
174 
175         // 2. update linked account
176         account.getPlainAttrs().clear();
177         account.setUsername("LinkedUsernameUpdated");
178         account.getPlainAttrs().add(attr("email", "UPDATED_EMAIL@syncope.apache.org"));
179         account.getPlainAttrs().add(attr("surname", "UPDATED_SURNAME"));
180         user.getLinkedAccounts().clear();
181         user.getLinkedAccounts().add(account);
182 
183         user = updateUser(user).getEntity();
184         assertEquals(1, user.getLinkedAccounts().size());
185         assertEquals("LinkedUsernameUpdated", user.getLinkedAccounts().get(0).getUsername());
186         assertEquals("UPDATED_SURNAME", account.getPlainAttr("surname").get().getValues().get(0));
187 
188         // 3. remove linked account from user
189         user.getLinkedAccounts().clear();
190         user = updateUser(user).getEntity();
191         assertTrue(user.getLinkedAccounts().isEmpty());
192     }
193 
194     @Test
195     public void createWithoutLinkedAccountThenAdd() throws NamingException {
196         // 1. create user without linked account
197         UserCR userCR = UserITCase.getSample(
198                 "linkedAccount" + RandomStringUtils.randomNumeric(5) + "@syncope.apache.org");
199         String connObjectKeyValue = "uid=" + userCR.getUsername() + ",ou=People,o=isp";
200 
201         UserTO user = createUser(userCR).getEntity();
202         assertNotNull(user.getKey());
203         assertTrue(user.getLinkedAccounts().isEmpty());
204 
205         PagedResult<PropagationTaskTO> tasks = TASK_SERVICE.search(
206                 new TaskQuery.Builder(TaskType.PROPAGATION).resource(RESOURCE_NAME_LDAP).
207                         anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
208         assertEquals(0, tasks.getTotalCount());
209 
210         assertNull(getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, connObjectKeyValue));
211 
212         // 2. add linked account to user
213         UserUR userUR = new UserUR();
214         userUR.setKey(user.getKey());
215 
216         LinkedAccountTO account = new LinkedAccountTO.Builder(RESOURCE_NAME_LDAP, connObjectKeyValue).build();
217         account.getPlainAttrs().add(attr("surname", "LINKED_SURNAME"));
218         account.setPassword("Password123");
219         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(account).build());
220 
221         user = updateUser(userUR).getEntity();
222         assertEquals(1, user.getLinkedAccounts().size());
223 
224         // 3. verify that propagation task was generated and that account is found on resource
225         tasks = TASK_SERVICE.search(
226                 new TaskQuery.Builder(TaskType.PROPAGATION).resource(RESOURCE_NAME_LDAP).
227                         anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
228         assertEquals(1, tasks.getTotalCount());
229         assertEquals(connObjectKeyValue, tasks.getResult().get(0).getConnObjectKey());
230         assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
231         assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
232 
233         ConnObject ldapObj = RESOURCE_SERVICE.readConnObject(
234                 RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), connObjectKeyValue);
235         assertNotNull(ldapObj);
236         assertEquals(user.getPlainAttr("email").get().getValues(), ldapObj.getAttr("mail").get().getValues());
237         assertEquals("LINKED_SURNAME", ldapObj.getAttr("sn").get().getValues().get(0));
238     }
239 
240     @Test
241     public void createWithoutLinkedAccountThenAddAndUpdatePassword() throws NamingException {
242         // 1. set the return value parameter to true
243         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "return.password.value", true);
244 
245         // 2. create user without linked account
246         UserCR userCR = UserITCase.getSample(
247                 "linkedAccount" + RandomStringUtils.randomNumeric(5) + "@syncope.apache.org");
248         String connObjectKeyValue = "uid=" + userCR.getUsername() + ",ou=People,o=isp";
249 
250         UserTO user = createUser(userCR).getEntity();
251         assertNotNull(user.getKey());
252         assertTrue(user.getLinkedAccounts().isEmpty());
253 
254         // 3. add linked account to user without password
255         UserUR userUR = new UserUR();
256         userUR.setKey(user.getKey());
257 
258         LinkedAccountTO account = new LinkedAccountTO.Builder(RESOURCE_NAME_LDAP, connObjectKeyValue).build();
259         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(account).build());
260 
261         user = updateUser(userUR).getEntity();
262         assertEquals(1, user.getLinkedAccounts().size());
263         assertNull(user.getLinkedAccounts().get(0).getPassword());
264 
265         // 4. update linked account with adding a password
266         account.setPassword("Password123");
267         userUR = new UserUR();
268         userUR.setKey(user.getKey());
269         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(account).build());
270         user = updateUser(userUR).getEntity();
271         assertNotNull(user.getLinkedAccounts().get(0).getPassword());
272 
273         // 5. update linked account  password
274         String beforeUpdatePassword = user.getLinkedAccounts().get(0).getPassword();
275         account.setPassword("Password123Updated");
276         userUR = new UserUR();
277         userUR.setKey(user.getKey());
278 
279         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(account).build());
280         user = updateUser(userUR).getEntity();
281         assertNotNull(user.getLinkedAccounts().get(0).getPassword());
282         assertNotEquals(beforeUpdatePassword, user.getLinkedAccounts().get(0).getPassword());
283 
284         // 6. set linked account password to null
285         account.setPassword(null);
286         userUR = new UserUR();
287         userUR.setKey(user.getKey());
288 
289         userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(account).build());
290         user = updateUser(userUR).getEntity();
291         assertNull(user.getLinkedAccounts().get(0).getPassword());
292 
293         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "return.password.value", false);
294     }
295 
296     @Test
297     public void push() {
298         // 0a. read configured cipher algorithm in order to be able to restore it at the end of test
299         String origpwdCipherAlgo = confParamOps.get(SyncopeConstants.MASTER_DOMAIN,
300                 "password.cipher.algorithm", null, String.class);
301 
302         // 0b. set AES password cipher algorithm
303         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", "AES");
304 
305         String userKey = null;
306         String connObjectKeyValue = UUID.randomUUID().toString();
307         try {
308             // 1. create user with linked account
309             UserCR userCR = UserITCase.getSample(
310                     "linkedAccount" + RandomStringUtils.randomNumeric(5) + "@syncope.apache.org");
311 
312             LinkedAccountTO account = new LinkedAccountTO.Builder(RESOURCE_NAME_REST, connObjectKeyValue).build();
313             userCR.getLinkedAccounts().add(account);
314 
315             UserTO user = createUser(userCR).getEntity();
316             userKey = user.getKey();
317             assertNotNull(userKey);
318             assertNotEquals(userKey, connObjectKeyValue);
319 
320             // 2. verify that account is found on resource
321             PagedResult<PropagationTaskTO> tasks = TASK_SERVICE.search(
322                     new TaskQuery.Builder(TaskType.PROPAGATION).resource(RESOURCE_NAME_REST).
323                             anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
324             assertEquals(1, tasks.getTotalCount());
325             assertEquals(connObjectKeyValue, tasks.getResult().get(0).getConnObjectKey());
326             assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
327             assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
328 
329             WebClient webClient = WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/users/" + connObjectKeyValue).
330                     accept(MediaType.APPLICATION_JSON_TYPE);
331             Response response = webClient.get();
332             assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
333 
334             // 3. remove account from resource
335             response = webClient.delete();
336             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
337 
338             response = webClient.get();
339             assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
340 
341             // 4. create PushTask for the user above
342             PushTaskTO sendUser = new PushTaskTO();
343             sendUser.setName("Send User " + user.getUsername());
344             sendUser.setResource(RESOURCE_NAME_REST);
345             sendUser.setUnmatchingRule(UnmatchingRule.PROVISION);
346             sendUser.setMatchingRule(MatchingRule.UPDATE);
347             sendUser.setSourceRealm(SyncopeConstants.ROOT_REALM);
348             sendUser.getFilters().put(AnyTypeKind.USER.name(), "username==" + user.getUsername());
349             sendUser.setPerformCreate(true);
350             sendUser.setPerformUpdate(true);
351 
352             response = TASK_SERVICE.create(TaskType.PUSH, sendUser);
353             sendUser = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
354             assertNotNull(sendUser);
355 
356             // 5. execute PushTask
357             AbstractTaskITCase.execProvisioningTask(
358                     TASK_SERVICE, TaskType.PUSH, sendUser.getKey(), MAX_WAIT_SECONDS, false);
359 
360             TaskTO task = TASK_SERVICE.read(TaskType.PUSH, sendUser.getKey(), true);
361             assertEquals(1, task.getExecutions().size());
362             assertEquals(ExecStatus.SUCCESS.name(), task.getExecutions().get(0).getStatus());
363 
364             tasks = TASK_SERVICE.search(
365                     new TaskQuery.Builder(TaskType.PROPAGATION).resource(RESOURCE_NAME_REST).
366                             anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
367             assertEquals(3, tasks.getTotalCount());
368 
369             // 6. verify that both user and account are now found on resource
370             response = webClient.get();
371             assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
372 
373             webClient = WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/users/" + userKey).
374                     accept(MediaType.APPLICATION_JSON_TYPE);
375             response = webClient.get();
376             assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
377         } finally {
378             // restore initial cipher algorithm
379             confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", origpwdCipherAlgo);
380 
381             // delete user and accounts
382             if (userKey != null) {
383                 WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/users/" + connObjectKeyValue).delete();
384                 WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/users/" + userKey).delete();
385 
386                 USER_SERVICE.delete(userKey);
387             }
388         }
389     }
390 
391     @Test
392     public void pull() {
393         // -----------------------------
394         // Add a custom policy with correlation rule
395         // -----------------------------
396         ResourceTO restResource = RESOURCE_SERVICE.read(RESOURCE_NAME_REST);
397         if (restResource.getPullPolicy() == null) {
398             ImplementationTO rule = null;
399             try {
400                 rule = IMPLEMENTATION_SERVICE.read(
401                         IdMImplementationType.PULL_CORRELATION_RULE, "LinkedAccountSamplePullCorrelationRule");
402             } catch (SyncopeClientException e) {
403                 if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
404                     rule = new ImplementationTO();
405                     rule.setKey("LinkedAccountSamplePullCorrelationRule");
406                     rule.setEngine(ImplementationEngine.JAVA);
407                     rule.setType(IdMImplementationType.PULL_CORRELATION_RULE);
408                     rule.setBody(POJOHelper.serialize(new LinkedAccountSamplePullCorrelationRuleConf()));
409                     Response response = IMPLEMENTATION_SERVICE.create(rule);
410                     rule = IMPLEMENTATION_SERVICE.read(
411                             rule.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
412                     assertNotNull(rule.getKey());
413                 }
414             }
415             assertNotNull(rule);
416 
417             PullPolicyTO policy = new PullPolicyTO();
418             policy.setName("Linked Account sample Pull policy");
419             policy.getCorrelationRules().put(AnyTypeKind.USER.name(), rule.getKey());
420             Response response = POLICY_SERVICE.create(PolicyType.PULL, policy);
421             policy = POLICY_SERVICE.read(PolicyType.PULL, response.getHeaderString(RESTHeaders.RESOURCE_KEY));
422             assertNotNull(policy.getKey());
423 
424             restResource.setPullPolicy(policy.getKey());
425             RESOURCE_SERVICE.update(restResource);
426         }
427 
428         // -----------------------------
429         // -----------------------------
430         // Add a pull task
431         // -----------------------------
432         String pullTaskKey;
433 
434         PagedResult<PullTaskTO> tasks = TASK_SERVICE.search(
435                 new TaskQuery.Builder(TaskType.PULL).resource(RESOURCE_NAME_REST).build());
436         if (tasks.getTotalCount() > 0) {
437             pullTaskKey = tasks.getResult().get(0).getKey();
438         } else {
439             PullTaskTO task = new PullTaskTO();
440             task.setDestinationRealm(SyncopeConstants.ROOT_REALM);
441             task.setName("Linked Account Pull Task");
442             task.setActive(true);
443             task.setResource(RESOURCE_NAME_REST);
444             task.setPullMode(PullMode.INCREMENTAL);
445             task.setPerformCreate(true);
446             task.setPerformUpdate(true);
447             task.setPerformDelete(true);
448             task.setSyncStatus(true);
449 
450             Response response = TASK_SERVICE.create(TaskType.PULL, task);
451             task = TASK_SERVICE.read(TaskType.PULL, response.getHeaderString(RESTHeaders.RESOURCE_KEY), false);
452             assertNotNull(task.getKey());
453             pullTaskKey = task.getKey();
454         }
455         assertNotNull(pullTaskKey);
456         // -----------------------------
457 
458         // 1. create REST users
459         WebClient webClient = WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/users").
460                 accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE);
461 
462         ObjectNode user = JSON_MAPPER.createObjectNode();
463         user.put("username", "linkedaccount1");
464         user.put("password", "Password123");
465         user.put("firstName", "Pasquale");
466         user.put("surname", "Vivaldi");
467         user.put("email", "vivaldi@syncope.org");
468 
469         Response response = webClient.post(user.toString());
470         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
471         String user1Key = StringUtils.substringAfterLast(response.getHeaderString(HttpHeaders.LOCATION), "/");
472         assertNotNull(user1Key);
473 
474         user = JSON_MAPPER.createObjectNode();
475         user.put("username", "vivaldi");
476         user.put("password", "Password123");
477         user.put("firstName", "Giovannino");
478         user.put("surname", "Vivaldi");
479         user.put("email", "vivaldi@syncope.org");
480 
481         response = webClient.post(user.toString());
482         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
483         String user2Key = StringUtils.substringAfterLast(response.getHeaderString(HttpHeaders.LOCATION), "/");
484         assertNotNull(user2Key);
485 
486         user = JSON_MAPPER.createObjectNode();
487         user.put("username", "not.vivaldi");
488         user.put("password", "Password123");
489         user.put("email", "not.vivaldi@syncope.org");
490 
491         response = webClient.post(user.toString());
492         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
493         String user3Key = StringUtils.substringAfterLast(response.getHeaderString(HttpHeaders.LOCATION), "/");
494         assertNotNull(user3Key);
495 
496         // 2. execute pull task and verify linked accounts were pulled
497         try {
498             List<LinkedAccountTO> accounts = USER_SERVICE.read("vivaldi").getLinkedAccounts();
499             assertTrue(accounts.isEmpty());
500 
501             ExecTO exec = AbstractTaskITCase.execProvisioningTask(
502                     TASK_SERVICE, TaskType.PULL, pullTaskKey, MAX_WAIT_SECONDS, false);
503             assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus()));
504             assertTrue(exec.getMessage().contains("Accounts created"));
505 
506             accounts = USER_SERVICE.read("vivaldi").getLinkedAccounts();
507             assertEquals(3, accounts.size());
508 
509             Optional<LinkedAccountTO> firstAccount = accounts.stream().
510                     filter(account -> user1Key.equals(account.getConnObjectKeyValue())).
511                     findFirst();
512             assertTrue(firstAccount.isPresent());
513             assertFalse(firstAccount.get().isSuspended());
514             assertEquals(RESOURCE_NAME_REST, firstAccount.get().getResource());
515             assertEquals("linkedaccount1", firstAccount.get().getUsername());
516             assertEquals("Pasquale", firstAccount.get().getPlainAttr("firstname").get().getValues().get(0));
517 
518             Optional<LinkedAccountTO> secondAccount = accounts.stream().
519                     filter(account -> user2Key.equals(account.getConnObjectKeyValue())).
520                     findFirst();
521             assertTrue(secondAccount.isPresent());
522             assertFalse(secondAccount.get().isSuspended());
523             assertEquals(RESOURCE_NAME_REST, secondAccount.get().getResource());
524             assertNull(secondAccount.get().getUsername());
525             assertEquals("Giovannino", secondAccount.get().getPlainAttr("firstname").get().getValues().get(0));
526 
527             Optional<LinkedAccountTO> thirdAccount = accounts.stream().
528                     filter(account -> user3Key.equals(account.getConnObjectKeyValue())).
529                     filter(account -> "not.vivaldi".equals(account.getUsername())).
530                     findFirst();
531             assertTrue(thirdAccount.isPresent());
532             assertFalse(thirdAccount.get().isSuspended());
533             assertEquals(RESOURCE_NAME_REST, thirdAccount.get().getResource());
534             assertEquals("not.vivaldi", thirdAccount.get().getUsername());
535 
536             // 3. update / remove REST users
537             response = webClient.path(user1Key).delete();
538             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
539 
540             user = JSON_MAPPER.createObjectNode();
541             user.put("username", "linkedaccount2");
542             response = webClient.replacePath(user2Key).put(user.toString());
543             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
544 
545             user = JSON_MAPPER.createObjectNode();
546             user.put("status", "INACTIVE");
547             response = webClient.replacePath(user3Key).put(user.toString());
548             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
549 
550             // 4. execute pull task again and verify linked accounts were pulled
551             exec = AbstractTaskITCase.execProvisioningTask(
552                     TASK_SERVICE, TaskType.PULL, pullTaskKey, MAX_WAIT_SECONDS, false);
553             assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(exec.getStatus()));
554             assertTrue(exec.getMessage().contains("Accounts updated"));
555             assertTrue(exec.getMessage().contains("Accounts deleted"));
556 
557             accounts = USER_SERVICE.read("vivaldi").getLinkedAccounts();
558             assertEquals(2, accounts.size());
559 
560             firstAccount = accounts.stream().
561                     filter(account -> user1Key.equals(account.getConnObjectKeyValue())).
562                     findFirst();
563             assertFalse(firstAccount.isPresent());
564 
565             secondAccount = accounts.stream().
566                     filter(account -> user2Key.equals(account.getConnObjectKeyValue())).
567                     findFirst();
568             assertTrue(secondAccount.isPresent());
569             assertFalse(secondAccount.get().isSuspended());
570             assertEquals(user2Key, secondAccount.get().getConnObjectKeyValue());
571             assertEquals("linkedaccount2", secondAccount.get().getUsername());
572 
573             thirdAccount = accounts.stream().
574                     filter(account -> "not.vivaldi".equals(account.getUsername())).
575                     findFirst();
576             assertTrue(thirdAccount.isPresent());
577             assertTrue(thirdAccount.get().isSuspended());
578             assertEquals(user3Key, thirdAccount.get().getConnObjectKeyValue());
579         } finally {
580             // clean up
581             UserUR patch = new UserUR();
582             patch.setKey(LinkedAccountSamplePullCorrelationRule.VIVALDI_KEY);
583             patch.getLinkedAccounts().add(new LinkedAccountUR.Builder().
584                     operation(PatchOperation.DELETE).
585                     linkedAccountTO(new LinkedAccountTO.Builder(RESOURCE_NAME_REST, user2Key).build()).
586                     build());
587             patch.getLinkedAccounts().add(new LinkedAccountUR.Builder().
588                     operation(PatchOperation.DELETE).
589                     linkedAccountTO(new LinkedAccountTO.Builder(RESOURCE_NAME_REST, user3Key).build()).
590                     build());
591             USER_SERVICE.update(patch);
592 
593             webClient.replacePath(user2Key).delete();
594             webClient.replacePath(user3Key).delete();
595         }
596     }
597 }