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  import static org.junit.jupiter.api.Assertions.fail;
28  import static org.junit.jupiter.api.Assumptions.assumeFalse;
29  
30  import java.io.IOException;
31  import java.nio.charset.StandardCharsets;
32  import java.util.Base64;
33  import java.util.Collection;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Optional;
37  import java.util.Set;
38  import java.util.UUID;
39  import javax.naming.NamingException;
40  import javax.ws.rs.HttpMethod;
41  import javax.ws.rs.core.GenericType;
42  import javax.ws.rs.core.MediaType;
43  import javax.ws.rs.core.Response;
44  import org.apache.commons.lang3.tuple.Triple;
45  import org.apache.cxf.helpers.IOUtils;
46  import org.apache.cxf.jaxrs.client.WebClient;
47  import org.apache.syncope.client.lib.SyncopeClient;
48  import org.apache.syncope.common.lib.Attr;
49  import org.apache.syncope.common.lib.SyncopeClientException;
50  import org.apache.syncope.common.lib.SyncopeConstants;
51  import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
52  import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
53  import org.apache.syncope.common.lib.request.AttrPatch;
54  import org.apache.syncope.common.lib.request.GroupCR;
55  import org.apache.syncope.common.lib.request.MembershipUR;
56  import org.apache.syncope.common.lib.request.PasswordPatch;
57  import org.apache.syncope.common.lib.request.ResourceAR;
58  import org.apache.syncope.common.lib.request.StringPatchItem;
59  import org.apache.syncope.common.lib.request.StringReplacePatchItem;
60  import org.apache.syncope.common.lib.request.UserCR;
61  import org.apache.syncope.common.lib.request.UserUR;
62  import org.apache.syncope.common.lib.to.ConnObject;
63  import org.apache.syncope.common.lib.to.GroupTO;
64  import org.apache.syncope.common.lib.to.ImplementationTO;
65  import org.apache.syncope.common.lib.to.Item;
66  import org.apache.syncope.common.lib.to.Mapping;
67  import org.apache.syncope.common.lib.to.MembershipTO;
68  import org.apache.syncope.common.lib.to.PropagationStatus;
69  import org.apache.syncope.common.lib.to.ProvisioningResult;
70  import org.apache.syncope.common.lib.to.RealmTO;
71  import org.apache.syncope.common.lib.to.ResourceTO;
72  import org.apache.syncope.common.lib.to.RoleTO;
73  import org.apache.syncope.common.lib.to.UserTO;
74  import org.apache.syncope.common.lib.types.AnyTypeKind;
75  import org.apache.syncope.common.lib.types.CipherAlgorithm;
76  import org.apache.syncope.common.lib.types.ClientExceptionType;
77  import org.apache.syncope.common.lib.types.ExecStatus;
78  import org.apache.syncope.common.lib.types.IdMImplementationType;
79  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
80  import org.apache.syncope.common.lib.types.IdRepoImplementationType;
81  import org.apache.syncope.common.lib.types.ImplementationEngine;
82  import org.apache.syncope.common.lib.types.MappingPurpose;
83  import org.apache.syncope.common.lib.types.PatchOperation;
84  import org.apache.syncope.common.lib.types.PolicyType;
85  import org.apache.syncope.common.lib.types.ResourceAssociationAction;
86  import org.apache.syncope.common.rest.api.RESTHeaders;
87  import org.apache.syncope.common.rest.api.beans.RealmQuery;
88  import org.apache.syncope.common.rest.api.service.UserService;
89  import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
90  import org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagationActions;
91  import org.apache.syncope.core.provisioning.java.propagation.GenerateRandomPasswordPropagationActions;
92  import org.apache.syncope.core.provisioning.java.propagation.LDAPPasswordPropagationActions;
93  import org.apache.syncope.core.spring.security.Encryptor;
94  import org.apache.syncope.fit.AbstractITCase;
95  import org.identityconnectors.framework.common.objects.Name;
96  import org.identityconnectors.framework.common.objects.OperationalAttributes;
97  import org.junit.jupiter.api.BeforeAll;
98  import org.junit.jupiter.api.Test;
99  import org.springframework.dao.EmptyResultDataAccessException;
100 import org.springframework.jdbc.core.JdbcTemplate;
101 
102 public class UserIssuesITCase extends AbstractITCase {
103 
104     @BeforeAll
105     public static void testPropagationActionsSetup() {
106         ImplementationTO propagationActions = null;
107         try {
108             propagationActions = IMPLEMENTATION_SERVICE.read(
109                     IdMImplementationType.PROPAGATION_ACTIONS, LDAPPasswordPropagationActions.class.getSimpleName());
110         } catch (SyncopeClientException e) {
111             if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
112                 propagationActions = new ImplementationTO();
113                 propagationActions.setKey(LDAPPasswordPropagationActions.class.getSimpleName());
114                 propagationActions.setEngine(ImplementationEngine.JAVA);
115                 propagationActions.setType(IdMImplementationType.PROPAGATION_ACTIONS);
116                 propagationActions.setBody(LDAPPasswordPropagationActions.class.getName());
117                 Response response = IMPLEMENTATION_SERVICE.create(propagationActions);
118                 propagationActions = IMPLEMENTATION_SERVICE.read(
119                         propagationActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
120             }
121         }
122         assertNotNull(propagationActions);
123     }
124 
125     @Test
126     public void issue186() {
127         // 1. create an user with strict mandatory attributes only
128         UserCR userCR = new UserCR();
129         userCR.setRealm(SyncopeConstants.ROOT_REALM);
130         String userId = getUUIDString() + "issue186@syncope.apache.org";
131         userCR.setUsername(userId);
132         userCR.setPassword("password123");
133 
134         userCR.getPlainAttrs().add(attr("userId", userId));
135         userCR.getPlainAttrs().add(attr("fullname", userId));
136         userCR.getPlainAttrs().add(attr("surname", userId));
137 
138         UserTO userTO = createUser(userCR).getEntity();
139         assertNotNull(userTO);
140         assertTrue(userTO.getResources().isEmpty());
141 
142         // 2. update assigning a resource forcing mandatory constraints: must fail with RequiredValuesMissing
143         UserUR userUR = new UserUR.Builder(userTO.getKey()).
144                 password(new PasswordPatch.Builder().value("newPassword123").build()).
145                 resource(new StringPatchItem.Builder().
146                         operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS2).build()).
147                 build();
148 
149         try {
150             userTO = updateUser(userUR).getEntity();
151             fail("This should not happen");
152         } catch (SyncopeClientException e) {
153             assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
154         }
155 
156         // 3. update assigning a resource NOT forcing mandatory constraints
157         // AND priority: must fail with PropagationException
158         userUR = new UserUR();
159         userUR.setKey(userTO.getKey());
160         userUR.setPassword(new PasswordPatch.Builder().value("newPassword123").build());
161         userUR.getResources().add(new StringPatchItem.Builder().
162                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS1).build());
163 
164         ProvisioningResult<UserTO> result = updateUser(userUR);
165         assertNotNull(result.getPropagationStatuses().get(0).getFailureReason());
166         userTO = result.getEntity();
167 
168         // 4. update assigning a resource NOT forcing mandatory constraints
169         // BUT not priority: must succeed
170         userUR = new UserUR();
171         userUR.setKey(userTO.getKey());
172         userUR.setPassword(new PasswordPatch.Builder().value("newPassword123456").build());
173         userUR.getResources().add(new StringPatchItem.Builder().
174                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_CSV).build());
175 
176         updateUser(userUR);
177     }
178 
179     @Test
180     public void issue213() {
181         UserCR userCR = UserITCase.getUniqueSample("issue213@syncope.apache.org");
182         userCR.getResources().add(RESOURCE_NAME_TESTDB);
183 
184         UserTO userTO = createUser(userCR).getEntity();
185         assertNotNull(userTO);
186         assertEquals(1, userTO.getResources().size());
187 
188         JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
189         String username = queryForObject(
190                 jdbcTemplate, MAX_WAIT_SECONDS, "SELECT id FROM test WHERE id=?", String.class, userTO.getUsername());
191         assertEquals(userTO.getUsername(), username);
192 
193         UserUR userUR = new UserUR();
194         userUR.setKey(userTO.getKey());
195         userUR.getResources().add(
196                 new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(RESOURCE_NAME_TESTDB).build());
197 
198         userTO = updateUser(userUR).getEntity();
199         assertTrue(userTO.getResources().isEmpty());
200 
201         Exception exception = null;
202         try {
203             jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", String.class, userTO.getUsername());
204         } catch (EmptyResultDataAccessException e) {
205             exception = e;
206         }
207         assertNotNull(exception);
208     }
209 
210     @Test
211     public void issue234() {
212         UserCR inUserTO = UserITCase.getUniqueSample("issue234@syncope.apache.org");
213         inUserTO.getResources().add(RESOURCE_NAME_LDAP);
214 
215         UserTO userTO = createUser(inUserTO).getEntity();
216         assertNotNull(userTO);
217 
218         UserUR userUR = new UserUR();
219 
220         userUR.setKey(userTO.getKey());
221         userUR.setUsername(new StringReplacePatchItem.Builder().value('1' + userTO.getUsername()).build());
222 
223         userTO = updateUser(userUR).getEntity();
224         assertNotNull(userTO);
225         assertEquals('1' + inUserTO.getUsername(), userTO.getUsername());
226     }
227 
228     @Test
229     public void issue280() {
230         UserCR userCR = UserITCase.getUniqueSample("issue280@syncope.apache.org");
231         userCR.getResources().clear();
232         userCR.getMemberships().clear();
233 
234         UserTO userTO = createUser(userCR).getEntity();
235         assertNotNull(userTO);
236 
237         UserUR userUR = new UserUR();
238         userUR.setKey(userTO.getKey());
239         userUR.setPassword(new PasswordPatch.Builder().onSyncope(false).
240                 resource(RESOURCE_NAME_TESTDB).value("123password").build());
241         userUR.getResources().add(new StringPatchItem.Builder().
242                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_TESTDB).build());
243 
244         ProvisioningResult<UserTO> result = updateUser(userUR);
245         assertNotNull(result);
246 
247         List<PropagationStatus> propagations = result.getPropagationStatuses();
248         assertNotNull(propagations);
249         assertEquals(1, propagations.size());
250 
251         assertEquals(ExecStatus.SUCCESS, propagations.get(0).getStatus());
252 
253         String resource = propagations.get(0).getResource();
254         assertEquals(RESOURCE_NAME_TESTDB, resource);
255     }
256 
257     @Test
258     public void issue281() {
259         UserCR userCR = UserITCase.getUniqueSample("issue281@syncope.apache.org");
260         userCR.getResources().clear();
261         userCR.getMemberships().clear();
262         userCR.getResources().add(RESOURCE_NAME_CSV);
263 
264         ProvisioningResult<UserTO> result = createUser(userCR);
265         assertNotNull(result);
266 
267         List<PropagationStatus> propagations = result.getPropagationStatuses();
268         assertNotNull(propagations);
269         assertEquals(1, propagations.size());
270         assertNotEquals(ExecStatus.SUCCESS, propagations.get(0).getStatus());
271 
272         String resource = propagations.get(0).getResource();
273         assertEquals(RESOURCE_NAME_CSV, resource);
274     }
275 
276     @Test
277     public void issue288() {
278         UserCR userTO = UserITCase.getSample("issue288@syncope.apache.org");
279         userTO.getPlainAttrs().add(attr("aLong", "STRING"));
280 
281         try {
282             createUser(userTO);
283             fail("This should not happen");
284         } catch (SyncopeClientException e) {
285             assertEquals(ClientExceptionType.InvalidValues, e.getType());
286         }
287     }
288 
289     @Test
290     public void issueSYNCOPE108() {
291         UserCR userCR = UserITCase.getUniqueSample("syncope108@syncope.apache.org");
292         userCR.getResources().clear();
293         userCR.getMemberships().clear();
294         userCR.getVirAttrs().clear();
295         userCR.getAuxClasses().add("csv");
296 
297         userCR.getMemberships().add(new MembershipTO.Builder("0626100b-a4ba-4e00-9971-86fad52a6216").build());
298         userCR.getMemberships().add(new MembershipTO.Builder("ba9ed509-b1f5-48ab-a334-c8530a6422dc").build());
299 
300         userCR.getResources().add(RESOURCE_NAME_CSV);
301 
302         UserTO userTO = createUser(userCR).getEntity();
303         assertNotNull(userTO);
304         assertEquals(2, userTO.getMemberships().size());
305         assertEquals(1, userTO.getResources().size());
306 
307         ConnObject connObjectTO =
308                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
309         assertNotNull(connObjectTO);
310 
311         // -----------------------------------
312         // Remove the first membership: de-provisioning shouldn't happen
313         // -----------------------------------
314         UserUR userUR = new UserUR.Builder(userTO.getKey()).
315                 membership(new MembershipUR.Builder(userTO.getMemberships().get(0).getGroupKey()).
316                         operation(PatchOperation.DELETE).build()).
317                 build();
318 
319         userTO = updateUser(userUR).getEntity();
320         assertNotNull(userTO);
321         assertEquals(1, userTO.getMemberships().size());
322 
323         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
324         assertNotNull(connObjectTO);
325         // -----------------------------------
326 
327         // -----------------------------------
328         // Remove the resource assigned directly: de-provisioning shouldn't happen
329         // -----------------------------------
330         userUR = new UserUR();
331         userUR.setKey(userTO.getKey());
332 
333         userUR.getResources().add(new StringPatchItem.Builder().operation(PatchOperation.DELETE).
334                 value(userTO.getResources().iterator().next()).build());
335 
336         userTO = updateUser(userUR).getEntity();
337         assertNotNull(userTO);
338         assertEquals(1, userTO.getMemberships().size());
339         assertFalse(userTO.getResources().isEmpty());
340 
341         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
342         assertNotNull(connObjectTO);
343         // -----------------------------------
344 
345         // -----------------------------------
346         // Remove the first membership: de-provisioning should happen
347         // -----------------------------------
348         userUR = new UserUR.Builder(userTO.getKey()).
349                 membership(new MembershipUR.Builder(userTO.getMemberships().get(0).getGroupKey()).
350                         operation(PatchOperation.DELETE).build()).
351                 build();
352 
353         userTO = updateUser(userUR).getEntity();
354         assertNotNull(userTO);
355         assertTrue(userTO.getMemberships().isEmpty());
356         assertTrue(userTO.getResources().isEmpty());
357 
358         try {
359             RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
360             fail("Read should not succeeed");
361         } catch (SyncopeClientException e) {
362             assertEquals(ClientExceptionType.NotFound, e.getType());
363         }
364     }
365 
366     @Test
367     public void issueSYNCOPE185() {
368         // 1. create user with LDAP resource, succesfully propagated
369         UserCR userCR = UserITCase.getSample("syncope185@syncope.apache.org");
370         userCR.getVirAttrs().clear();
371         userCR.getResources().add(RESOURCE_NAME_LDAP);
372 
373         ProvisioningResult<UserTO> result = createUser(userCR);
374         assertNotNull(result);
375         assertFalse(result.getPropagationStatuses().isEmpty());
376         assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
377         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
378         UserTO userTO = result.getEntity();
379 
380         // 2. delete this user
381         USER_SERVICE.delete(userTO.getKey());
382 
383         // 3. try (and fail) to find this user on the external LDAP resource
384         try {
385             RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
386             fail("This entry should not be present on this resource");
387         } catch (SyncopeClientException e) {
388             assertEquals(ClientExceptionType.NotFound, e.getType());
389         }
390     }
391 
392     @Test()
393     public void issueSYNCOPE51() {
394         String originalCA = confParamOps.get(SyncopeConstants.MASTER_DOMAIN,
395                 "password.cipher.algorithm", null, String.class);
396         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", "MD5");
397 
398         UserCR userCR = UserITCase.getSample("syncope51@syncope.apache.org");
399         userCR.setPassword("password");
400 
401         try {
402             createUser(userCR);
403             fail("Create user should not succeed");
404         } catch (SyncopeClientException e) {
405             assertEquals(ClientExceptionType.NotFound, e.getType());
406             assertTrue(e.getElements().iterator().next().contains("MD5"));
407         } finally {
408             confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", originalCA);
409         }
410     }
411 
412     @Test
413     public void issueSYNCOPE267() {
414         // ----------------------------------
415         // create user and check virtual attribute value propagation
416         // ----------------------------------
417         UserCR userCR = UserITCase.getUniqueSample("syncope267@apache.org");
418         userCR.getVirAttrs().add(attr("virtualdata", "virtualvalue"));
419         userCR.getResources().clear();
420         userCR.getResources().add(RESOURCE_NAME_DBVIRATTR);
421 
422         ProvisioningResult<UserTO> result = createUser(userCR);
423         assertNotNull(result);
424         assertFalse(result.getPropagationStatuses().isEmpty());
425         assertEquals(RESOURCE_NAME_DBVIRATTR, result.getPropagationStatuses().get(0).getResource());
426         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
427         UserTO userTO = result.getEntity();
428 
429         ConnObject connObjectTO =
430                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_DBVIRATTR, AnyTypeKind.USER.name(), userTO.getKey());
431         assertNotNull(connObjectTO);
432         assertEquals("virtualvalue", connObjectTO.getAttr("USERNAME").get().getValues().get(0));
433         // ----------------------------------
434 
435         userTO = USER_SERVICE.read(userTO.getKey());
436 
437         assertNotNull(userTO);
438         assertEquals(1, userTO.getVirAttrs().size());
439         assertEquals("virtualvalue", userTO.getVirAttrs().iterator().next().getValues().get(0));
440     }
441 
442     @Test
443     public void issueSYNCOPE266() {
444         UserCR userCR = UserITCase.getUniqueSample("syncope266@apache.org");
445         userCR.getResources().clear();
446 
447         UserTO userTO = createUser(userCR).getEntity();
448         assertNotNull(userTO);
449 
450         UserUR userUR = new UserUR();
451         userUR.setKey(userTO.getKey());
452 
453         // this resource has not a mapping for Password
454         userUR.getResources().add(new StringPatchItem.Builder().
455                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_UPDATE).build());
456 
457         userTO = updateUser(userUR).getEntity();
458         assertNotNull(userTO);
459     }
460 
461     @Test
462     public void issueSYNCOPE279() {
463         UserCR userCR = UserITCase.getUniqueSample("syncope279@apache.org");
464         userCR.getResources().clear();
465         userCR.getResources().add(RESOURCE_NAME_TIMEOUT);
466         ProvisioningResult<UserTO> result = createUser(userCR);
467         assertEquals(RESOURCE_NAME_TIMEOUT, result.getPropagationStatuses().get(0).getResource());
468         assertNotNull(result.getPropagationStatuses().get(0).getFailureReason());
469         assertEquals(ExecStatus.FAILURE, result.getPropagationStatuses().get(0).getStatus());
470     }
471 
472     @Test
473     public void issueSYNCOPE122() {
474         // 1. create user on testdb and testdb2
475         UserCR userCR = UserITCase.getUniqueSample("syncope122@apache.org");
476         userCR.getResources().clear();
477 
478         userCR.getResources().add(RESOURCE_NAME_TESTDB);
479         userCR.getResources().add(RESOURCE_NAME_TESTDB2);
480 
481         UserTO userTO = createUser(userCR).getEntity();
482         assertNotNull(userTO);
483         assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
484         assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB2));
485 
486         String pwdOnSyncope = userTO.getPassword();
487 
488         ConnObject userOnDb = RESOURCE_SERVICE.readConnObject(
489                 RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
490         Attr pwdOnTestDbAttr = userOnDb.getAttr(OperationalAttributes.PASSWORD_NAME).get();
491         assertNotNull(pwdOnTestDbAttr);
492         assertNotNull(pwdOnTestDbAttr.getValues());
493         assertFalse(pwdOnTestDbAttr.getValues().isEmpty());
494         String pwdOnTestDb = pwdOnTestDbAttr.getValues().get(0);
495 
496         ConnObject userOnDb2 = RESOURCE_SERVICE.readConnObject(
497                 RESOURCE_NAME_TESTDB2, AnyTypeKind.USER.name(), userTO.getKey());
498         Attr pwdOnTestDb2Attr = userOnDb2.getAttr(OperationalAttributes.PASSWORD_NAME).get();
499         assertNotNull(pwdOnTestDb2Attr);
500         assertNotNull(pwdOnTestDb2Attr.getValues());
501         assertFalse(pwdOnTestDb2Attr.getValues().isEmpty());
502         String pwdOnTestDb2 = pwdOnTestDb2Attr.getValues().get(0);
503 
504         // 2. request to change password only on testdb (no Syncope, no testdb2)
505         UserUR userUR = new UserUR();
506         userUR.setKey(userTO.getKey());
507         userUR.setPassword(new PasswordPatch.Builder().value(UUID.randomUUID().toString()).onSyncope(false).
508                 resource(RESOURCE_NAME_TESTDB).build());
509 
510         ProvisioningResult<UserTO> result = updateUser(userUR);
511         userTO = result.getEntity();
512 
513         // 3a. Chech that only a single propagation took place
514         assertNotNull(result.getPropagationStatuses());
515         assertEquals(1, result.getPropagationStatuses().size());
516         assertEquals(RESOURCE_NAME_TESTDB, result.getPropagationStatuses().get(0).getResource());
517 
518         // 3b. verify that password hasn't changed on Syncope
519         assertEquals(pwdOnSyncope, userTO.getPassword());
520 
521         // 3c. verify that password *has* changed on testdb
522         userOnDb = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
523         Attr pwdOnTestDbAttrAfter = userOnDb.getAttr(OperationalAttributes.PASSWORD_NAME).get();
524         assertNotNull(pwdOnTestDbAttrAfter);
525         assertNotNull(pwdOnTestDbAttrAfter.getValues());
526         assertFalse(pwdOnTestDbAttrAfter.getValues().isEmpty());
527         assertNotEquals(pwdOnTestDb, pwdOnTestDbAttrAfter.getValues().get(0));
528 
529         // 3d. verify that password hasn't changed on testdb2
530         userOnDb2 = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_TESTDB2, AnyTypeKind.USER.name(), userTO.getKey());
531         Attr pwdOnTestDb2AttrAfter = userOnDb2.getAttr(OperationalAttributes.PASSWORD_NAME).get();
532         assertNotNull(pwdOnTestDb2AttrAfter);
533         assertNotNull(pwdOnTestDb2AttrAfter.getValues());
534         assertFalse(pwdOnTestDb2AttrAfter.getValues().isEmpty());
535         assertEquals(pwdOnTestDb2, pwdOnTestDb2AttrAfter.getValues().get(0));
536     }
537 
538     @Test
539     public void issueSYNCOPE136AES() {
540         // 1. read configured cipher algorithm in order to be able to restore it at the end of test
541         String origpwdCipherAlgo = confParamOps.get(SyncopeConstants.MASTER_DOMAIN,
542                 "password.cipher.algorithm", null, String.class);
543 
544         // 2. set AES password cipher algorithm
545         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", "AES");
546 
547         UserTO userTO = null;
548         try {
549             // 3. create user with no resources
550             UserCR userCR = UserITCase.getUniqueSample("syncope136_AES@apache.org");
551             userCR.getResources().clear();
552 
553             userTO = createUser(userCR).getEntity();
554             assertNotNull(userTO);
555 
556             // 4. update user, assign a propagation priority resource but don't provide any password
557             UserUR userUR = new UserUR();
558             userUR.setKey(userTO.getKey());
559             userUR.getResources().add(new StringPatchItem.Builder().
560                     operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_LDAP).build());
561             userUR.setPassword(new PasswordPatch.Builder().onSyncope(false).resource(RESOURCE_NAME_LDAP).build());
562 
563             ProvisioningResult<UserTO> result = updateUser(userUR);
564             assertNotNull(result);
565             userTO = result.getEntity();
566             assertNotNull(userTO);
567 
568             // 5. verify that propagation was successful
569             List<PropagationStatus> props = result.getPropagationStatuses();
570             assertNotNull(props);
571             assertEquals(1, props.size());
572             PropagationStatus prop = props.get(0);
573             assertNotNull(prop);
574             assertEquals(RESOURCE_NAME_LDAP, prop.getResource());
575             assertEquals(ExecStatus.SUCCESS, prop.getStatus());
576         } finally {
577             // restore initial cipher algorithm
578             confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", origpwdCipherAlgo);
579 
580             if (userTO != null) {
581                 deleteUser(userTO.getKey());
582             }
583         }
584     }
585 
586     @Test
587     public void issueSYNCOPE136Random() {
588         // 1. create user with no resources
589         UserCR userCR = UserITCase.getUniqueSample("syncope136_Random@apache.org");
590         userCR.getResources().clear();
591         UserTO userTO = createUser(userCR).getEntity();
592         assertNotNull(userTO);
593 
594         // 2. update user, assign a propagation priority resource but don't provide any password
595         UserUR userUR = new UserUR();
596         userUR.setKey(userTO.getKey());
597         userUR.getResources().add(new StringPatchItem.Builder().
598                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_LDAP).build());
599         userUR.setPassword(new PasswordPatch.Builder().onSyncope(false).resource(RESOURCE_NAME_LDAP).build());
600 
601         ProvisioningResult<UserTO> result = updateUser(userUR);
602         assertNotNull(result);
603 
604         // 3. verify that propagation was successful
605         List<PropagationStatus> props = result.getPropagationStatuses();
606         assertNotNull(props);
607         assertEquals(1, props.size());
608         PropagationStatus prop = props.get(0);
609         assertNotNull(prop);
610         assertEquals(RESOURCE_NAME_LDAP, prop.getResource());
611         assertEquals(ExecStatus.SUCCESS, prop.getStatus());
612     }
613 
614     @Test
615     public void issueSYNCOPE265() {
616         String[] userKeys = new String[] {
617             "1417acbe-cbf6-4277-9372-e75e04f97000",
618             "74cd8ece-715a-44a4-a736-e17b46c4e7e6",
619             "b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee",
620             "c9b2dec2-00a7-4855-97c0-d854842b4b24",
621             "823074dc-d280-436d-a7dd-07399fae48ec" };
622 
623         for (String userKey : userKeys) {
624             UserUR userUR = new UserUR();
625             userUR.setKey(userKey);
626             userUR.getPlainAttrs().add(attrAddReplacePatch("ctype", "a type"));
627             UserTO userTO = updateUser(userUR).getEntity();
628             assertEquals("a type", userTO.getPlainAttr("ctype").get().getValues().get(0));
629         }
630     }
631 
632     @Test
633     public void issueSYNCOPE354() {
634         // change resource-ldap group mapping for including uniqueMember (need for assertions below)
635         ResourceTO ldap = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP);
636         ldap.getProvision(AnyTypeKind.GROUP.name()).get().getMapping().getItems().stream().
637                 filter(item -> ("description".equals(item.getExtAttrName()))).
638                 forEach(item -> item.setExtAttrName("uniqueMember"));
639         RESOURCE_SERVICE.update(ldap);
640 
641         // 1. create group with LDAP resource
642         GroupCR groupCR = new GroupCR();
643         groupCR.setName("SYNCOPE354-" + getUUIDString());
644         groupCR.setRealm("/");
645         groupCR.getResources().add(RESOURCE_NAME_LDAP);
646 
647         GroupTO groupTO = createGroup(groupCR).getEntity();
648         assertNotNull(groupTO);
649 
650         // 2. create user with LDAP resource and membership of the above group
651         UserCR userCR = UserITCase.getUniqueSample("syncope354@syncope.apache.org");
652         userCR.getResources().add(RESOURCE_NAME_LDAP);
653         userCR.getMemberships().add(new MembershipTO.Builder(groupTO.getKey()).build());
654 
655         UserTO userTO = createUser(userCR).getEntity();
656         assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
657         assertNotNull(RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey()));
658 
659         // 3. read group on resource, check that user DN is included in uniqueMember
660         ConnObject connObj = RESOURCE_SERVICE.readConnObject(
661                 RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
662         assertNotNull(connObj);
663         assertTrue(connObj.getAttr("uniqueMember").get().getValues().
664                 contains("uid=" + userTO.getUsername() + ",ou=people,o=isp"));
665 
666         // 4. remove membership
667         UserUR userUR = new UserUR();
668         userUR.setKey(userTO.getKey());
669         userUR.getMemberships().add(new MembershipUR.Builder(userTO.getMemberships().get(0).getGroupKey()).
670                 operation(PatchOperation.DELETE).build());
671 
672         userTO = updateUser(userUR).getEntity();
673         assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
674 
675         // 5. read group on resource, check that user DN was removed from uniqueMember
676         connObj = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
677         assertNotNull(connObj);
678         assertFalse(connObj.getAttr("uniqueMember").get().getValues().
679                 contains("uid=" + userTO.getUsername() + ",ou=people,o=isp"));
680 
681         // 6. user has still the LDAP resource assigned - SYNCOPE-1222
682         userTO = USER_SERVICE.read(userTO.getKey());
683         assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
684         assertNotNull(RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey()));
685 
686         // 7. restore original resource-ldap group mapping
687         ldap.getProvision(AnyTypeKind.GROUP.name()).get().getMapping().getItems().stream().
688                 filter(item -> "uniqueMember".equals(item.getExtAttrName())).
689                 forEach(item -> item.setExtAttrName("description"));
690         RESOURCE_SERVICE.update(ldap);
691     }
692 
693     @Test
694     public void issueSYNCOPE357() throws IOException {
695         // 1. create group with LDAP resource
696         GroupCR groupCR = new GroupCR();
697         groupCR.setName("SYNCOPE357-" + getUUIDString());
698         groupCR.setRealm("/");
699         groupCR.getResources().add(RESOURCE_NAME_LDAP);
700 
701         GroupTO groupTO = createGroup(groupCR).getEntity();
702         assertNotNull(groupTO);
703 
704         // 2. create user with membership of the above group
705         UserCR userCR = UserITCase.getUniqueSample("syncope357@syncope.apache.org");
706         userCR.getPlainAttrs().add(attr("obscure", "valueToBeObscured"));
707         userCR.getPlainAttrs().add(attr("photo", Base64.getEncoder().encodeToString(
708                 IOUtils.readBytesFromStream(getClass().getResourceAsStream("/favicon.jpg")))));
709         userCR.getMemberships().add(new MembershipTO.Builder(groupTO.getKey()).build());
710 
711         UserTO userTO = createUser(userCR).getEntity();
712         assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
713         assertNotNull(userTO.getPlainAttr("obscure"));
714         assertNotNull(userTO.getPlainAttr("photo"));
715 
716         // 3. read user on resource
717         ConnObject connObj = RESOURCE_SERVICE.readConnObject(
718                 RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
719         assertNotNull(connObj);
720         Attr registeredAddress = connObj.getAttr("registeredAddress").get();
721         assertNotNull(registeredAddress);
722         assertEquals(userTO.getPlainAttr("obscure").get().getValues(), registeredAddress.getValues());
723         Optional<Attr> jpegPhoto = connObj.getAttr("jpegPhoto");
724         assertTrue(jpegPhoto.isPresent());
725         assertEquals(userTO.getPlainAttr("photo").get().getValues().get(0), jpegPhoto.get().getValues().get(0));
726 
727         // 4. remove group
728         GROUP_SERVICE.delete(groupTO.getKey());
729 
730         // 5. try to read user on resource: fail
731         try {
732             RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
733             fail("This should not happen");
734         } catch (SyncopeClientException e) {
735             assertEquals(ClientExceptionType.NotFound, e.getType());
736         }
737     }
738 
739     @Test
740     public void issueSYNCOPE383() {
741         // 1. create user without resources
742         UserCR userCR = UserITCase.getUniqueSample("syncope383@apache.org");
743         userCR.getResources().clear();
744         UserTO userTO = createUser(userCR).getEntity();
745         assertNotNull(userTO);
746 
747         // 2. assign resource without specifying a new pwd and check propagation failure
748         UserUR userUR = new UserUR();
749         userUR.setKey(userTO.getKey());
750         userUR.getResources().add(new StringPatchItem.Builder().
751                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_TESTDB).build());
752 
753         ProvisioningResult<UserTO> result = updateUser(userUR);
754         assertNotNull(result);
755         userTO = result.getEntity();
756         assertEquals(RESOURCE_NAME_TESTDB, userTO.getResources().iterator().next());
757         assertNotEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
758         assertNotNull(result.getPropagationStatuses().get(0).getFailureReason());
759         userTO = result.getEntity();
760 
761         // 3. request to change password only on testdb
762         userUR = new UserUR();
763         userUR.setKey(userTO.getKey());
764         userUR.setPassword(
765                 new PasswordPatch.Builder().value(getUUIDString() + "abbcbcbddd123").resource(RESOURCE_NAME_TESTDB).
766                         build());
767 
768         result = updateUser(userUR);
769         assertEquals(RESOURCE_NAME_TESTDB, userTO.getResources().iterator().next());
770         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
771     }
772 
773     @Test
774     public void issueSYNCOPE402() {
775         // 1. create an user with strict mandatory attributes only
776         UserCR userCR = new UserCR();
777         userCR.setRealm(SyncopeConstants.ROOT_REALM);
778         String userId = getUUIDString() + "syncope402@syncope.apache.org";
779         userCR.setUsername(userId);
780         userCR.setPassword("password123");
781 
782         userCR.getPlainAttrs().add(attr("userId", userId));
783         userCR.getPlainAttrs().add(attr("fullname", userId));
784         userCR.getPlainAttrs().add(attr("surname", userId));
785 
786         UserTO userTO = createUser(userCR).getEntity();
787         assertNotNull(userTO);
788         assertTrue(userTO.getResources().isEmpty());
789 
790         // 2. update assigning a resource NOT forcing mandatory constraints
791         // AND priority: must fail with PropagationException
792         UserUR userUR = new UserUR();
793         userUR.setKey(userTO.getKey());
794         userUR.setPassword(new PasswordPatch.Builder().value("newPassword123").build());
795         userUR.getResources().add(new StringPatchItem.Builder().
796                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS1).build());
797         userUR.getResources().add(new StringPatchItem.Builder().
798                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_TESTDB).build());
799         ProvisioningResult<UserTO> result = updateUser(userUR);
800 
801         PropagationStatus ws1PropagationStatus = result.getPropagationStatuses().stream().
802                 filter(propStatus -> RESOURCE_NAME_WS1.equals(propStatus.getResource())).
803                 findFirst().orElse(null);
804         assertNotNull(ws1PropagationStatus);
805         assertEquals(RESOURCE_NAME_WS1, ws1PropagationStatus.getResource());
806         assertNotNull(ws1PropagationStatus.getFailureReason());
807         assertEquals(ExecStatus.FAILURE, ws1PropagationStatus.getStatus());
808     }
809 
810     @Test
811     public void issueSYNCOPE420() throws IOException {
812         ImplementationTO logicActions;
813         try {
814             logicActions = IMPLEMENTATION_SERVICE.read(
815                     IdRepoImplementationType.LOGIC_ACTIONS, "DoubleValueLogicActions");
816         } catch (SyncopeClientException e) {
817             logicActions = new ImplementationTO();
818             logicActions.setKey("DoubleValueLogicActions");
819             logicActions.setEngine(ImplementationEngine.GROOVY);
820             logicActions.setType(IdRepoImplementationType.LOGIC_ACTIONS);
821             logicActions.setBody(org.apache.commons.io.IOUtils.toString(
822                     getClass().getResourceAsStream("/DoubleValueLogicActions.groovy"), StandardCharsets.UTF_8));
823             Response response = IMPLEMENTATION_SERVICE.create(logicActions);
824             logicActions = IMPLEMENTATION_SERVICE.read(
825                     logicActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
826         }
827         assertNotNull(logicActions);
828 
829         RealmTO realm = REALM_SERVICE.search(new RealmQuery.Builder().keyword("two").build()).getResult().get(0);
830         assertNotNull(realm);
831         realm.getActions().add(logicActions.getKey());
832         REALM_SERVICE.update(realm);
833 
834         UserCR userCR = UserITCase.getUniqueSample("syncope420@syncope.apache.org");
835         userCR.setRealm(realm.getFullPath());
836         userCR.getPlainAttrs().add(attr("makeItDouble", "3"));
837 
838         UserTO userTO = createUser(userCR).getEntity();
839         assertEquals("6", userTO.getPlainAttr("makeItDouble").get().getValues().get(0));
840 
841         UserUR userUR = new UserUR();
842         userUR.setKey(userTO.getKey());
843         userUR.getPlainAttrs().add(attrAddReplacePatch("makeItDouble", "7"));
844 
845         userTO = updateUser(userUR).getEntity();
846         assertEquals("14", userTO.getPlainAttr("makeItDouble").get().getValues().get(0));
847     }
848 
849     @Test
850     public void issueSYNCOPE426() {
851         UserCR userCR = UserITCase.getUniqueSample("syncope426@syncope.apache.org");
852         UserTO userTO = createUser(userCR).getEntity();
853         assertNotNull(userTO);
854 
855         UserUR userUR = new UserUR();
856         userUR.setKey(userTO.getKey());
857         userUR.setPassword(new PasswordPatch.Builder().value("anotherPassword123").build());
858         userTO = USER_SERVICE.update(userUR).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
859         }).getEntity();
860         assertNotNull(userTO);
861     }
862 
863     @Test
864     public void issueSYNCOPE435() {
865         // 1. create user without password
866         UserCR userCR = UserITCase.getUniqueSample("syncope435@syncope.apache.org");
867         userCR.setPassword(null);
868         userCR.setStorePassword(false);
869         UserTO userTO = createUser(userCR).getEntity();
870         assertNotNull(userTO);
871 
872         // 2. try to update user by subscribing a resource - works but propagation is not even attempted
873         UserUR userUR = new UserUR();
874         userUR.setKey(userTO.getKey());
875         userUR.getResources().add(new StringPatchItem.Builder().
876                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_WS1).build());
877 
878         ProvisioningResult<UserTO> result = updateUser(userUR);
879         assertNotNull(result);
880         userTO = result.getEntity();
881         assertEquals(Set.of(RESOURCE_NAME_WS1), userTO.getResources());
882         assertNotEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
883         assertTrue(result.getPropagationStatuses().get(0).getFailureReason().
884                 startsWith("Not attempted because there are mandatory attributes without value(s): [__PASSWORD__]"));
885     }
886 
887     @Test
888     public void issueSYNCOPE454() throws NamingException {
889         // 1. create user with LDAP resource (with 'Generate password if missing' enabled)
890         UserCR userCR = UserITCase.getUniqueSample("syncope454@syncope.apache.org");
891         userCR.getResources().add(RESOURCE_NAME_LDAP);
892         UserTO userTO = createUser(userCR).getEntity();
893         assertNotNull(userTO);
894 
895         // 2. read resource configuration for LDAP binding
896         ConnObject connObject =
897                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
898 
899         // 3. try (and succeed) to perform simple LDAP binding with provided password ('password123')
900         assertNotNull(getLdapRemoteObject(
901                 connObject.getAttr(Name.NAME).get().getValues().get(0),
902                 "password123",
903                 connObject.getAttr(Name.NAME).get().getValues().get(0)));
904 
905         // 4. update user without any password change request
906         UserUR userUR = new UserUR();
907         userUR.setKey(userTO.getKey());
908         userUR.setPassword(new PasswordPatch());
909         userUR.getPlainAttrs().add(attrAddReplacePatch("surname", "surname2"));
910 
911         USER_SERVICE.update(userUR);
912 
913         // 5. try (and succeed again) to perform simple LDAP binding: password has not changed
914         assertNotNull(getLdapRemoteObject(
915                 connObject.getAttr(Name.NAME).get().getValues().get(0),
916                 "password123",
917                 connObject.getAttr(Name.NAME).get().getValues().get(0)));
918     }
919 
920     @Test
921     public void issueSYNCOPE493() {
922         // 1.  create user and check that firstname is not propagated on resource with mapping for firstname set to NONE
923         UserCR userCR = UserITCase.getUniqueSample("493@test.org");
924         userCR.getResources().add(RESOURCE_NAME_WS1);
925         ProvisioningResult<UserTO> result = createUser(userCR);
926         assertNotNull(result);
927         assertEquals(1, result.getPropagationStatuses().size());
928         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
929         UserTO userTO = result.getEntity();
930 
931         ConnObject actual =
932                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS1, AnyTypeKind.USER.name(), userTO.getKey());
933         assertNotNull(actual);
934         // check if mapping attribute with purpose NONE really hasn't been propagated
935         assertFalse(actual.getAttr("NAME").isPresent());
936 
937         // 2.  update resource ws-target-resource-1
938         ResourceTO ws1 = RESOURCE_SERVICE.read(RESOURCE_NAME_WS1);
939         assertNotNull(ws1);
940 
941         Mapping ws1NewUMapping = ws1.getProvision(AnyTypeKind.USER.name()).get().getMapping();
942         // change purpose from NONE to BOTH
943         ws1NewUMapping.getItems().stream().
944                 filter(itemTO -> "firstname".equals(itemTO.getIntAttrName())).
945                 forEach(itemTO -> itemTO.setPurpose(MappingPurpose.BOTH));
946 
947         ws1.getProvision(AnyTypeKind.USER.name()).get().setMapping(ws1NewUMapping);
948 
949         RESOURCE_SERVICE.update(ws1);
950         ResourceTO newWs1 = RESOURCE_SERVICE.read(ws1.getKey());
951         assertNotNull(newWs1);
952 
953         // check for existence
954         Collection<Item> mapItems = newWs1.getProvision(AnyTypeKind.USER.name()).get().getMapping().getItems();
955         assertNotNull(mapItems);
956         assertEquals(7, mapItems.size());
957 
958         // 3.  update user and check firstname propagation
959         UserUR userUR = new UserUR();
960         userUR.setKey(userTO.getKey());
961         userUR.setPassword(new PasswordPatch());
962         userUR.getPlainAttrs().add(attrAddReplacePatch("firstname", "firstnameNew"));
963 
964         result = updateUser(userUR);
965         assertNotNull(userTO);
966         assertEquals(1, result.getPropagationStatuses().size());
967         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
968         userTO = result.getEntity();
969 
970         ConnObject newUser =
971                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_WS1, AnyTypeKind.USER.name(), userTO.getKey());
972 
973         assertNotNull(newUser.getAttr("NAME"));
974         assertEquals("firstnameNew", newUser.getAttr("NAME").get().getValues().get(0));
975 
976         // 4.  restore resource ws-target-resource-1 mapping
977         ws1NewUMapping = newWs1.getProvision(AnyTypeKind.USER.name()).get().getMapping();
978         // restore purpose from BOTH to NONE
979         for (Item itemTO : ws1NewUMapping.getItems()) {
980             if ("firstname".equals(itemTO.getIntAttrName())) {
981                 itemTO.setPurpose(MappingPurpose.NONE);
982             }
983         }
984 
985         newWs1.getProvision(AnyTypeKind.USER.name()).get().setMapping(ws1NewUMapping);
986 
987         RESOURCE_SERVICE.update(newWs1);
988     }
989 
990     @Test
991     public void issueSYNCOPE505DB() throws Exception {
992         // 1. create user
993         UserCR userCR = UserITCase.getUniqueSample("syncope505-db@syncope.apache.org");
994         userCR.setPassword("security123");
995         UserTO user = createUser(userCR).getEntity();
996         assertNotNull(user);
997         assertTrue(user.getResources().isEmpty());
998 
999         // 2. Add DBPasswordPropagationActions
1000         ImplementationTO propagationActions = new ImplementationTO();
1001         propagationActions.setKey(DBPasswordPropagationActions.class.getSimpleName());
1002         propagationActions.setEngine(ImplementationEngine.JAVA);
1003         propagationActions.setType(IdMImplementationType.PROPAGATION_ACTIONS);
1004         propagationActions.setBody(DBPasswordPropagationActions.class.getName());
1005         Response response = IMPLEMENTATION_SERVICE.create(propagationActions);
1006         propagationActions = IMPLEMENTATION_SERVICE.read(
1007                 propagationActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
1008         assertNotNull(propagationActions);
1009 
1010         ResourceTO resourceTO = RESOURCE_SERVICE.read(RESOURCE_NAME_TESTDB);
1011         assertNotNull(resourceTO);
1012         resourceTO.getPropagationActions().add(propagationActions.getKey());
1013         RESOURCE_SERVICE.update(resourceTO);
1014 
1015         // 3. Add a db resource to the User
1016         UserUR userUR = new UserUR();
1017         userUR.setKey(user.getKey());
1018         userUR.getResources().add(new StringPatchItem.Builder().
1019                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_TESTDB).build());
1020 
1021         userUR.setPassword(new PasswordPatch.Builder().onSyncope(false).resource(RESOURCE_NAME_TESTDB).build());
1022 
1023         user = updateUser(userUR).getEntity();
1024         assertNotNull(user);
1025         assertEquals(1, user.getResources().size());
1026 
1027         // 4. Check that the DB resource has the correct password
1028         JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
1029         String value = jdbcTemplate.queryForObject(
1030                 "SELECT PASSWORD FROM test WHERE ID=?", String.class, user.getUsername());
1031         assertEquals(Encryptor.getInstance().encode("security123", CipherAlgorithm.SHA1), value.toUpperCase());
1032 
1033         // 5. Remove DBPasswordPropagationActions
1034         resourceTO = RESOURCE_SERVICE.read(RESOURCE_NAME_TESTDB);
1035         assertNotNull(resourceTO);
1036         resourceTO.getPropagationActions().remove(propagationActions.getKey());
1037         RESOURCE_SERVICE.update(resourceTO);
1038     }
1039 
1040     @Test
1041     public void issueSYNCOPE505LDAP() throws Exception {
1042         // 1. create user
1043         UserCR userCR = UserITCase.getUniqueSample("syncope505-ldap@syncope.apache.org");
1044         userCR.setPassword("security123");
1045         UserTO user = createUser(userCR).getEntity();
1046         assertNotNull(user);
1047         assertTrue(user.getResources().isEmpty());
1048 
1049         // 2. Add LDAPPasswordPropagationActions
1050         ResourceTO resourceTO = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP);
1051         assertNotNull(resourceTO);
1052         resourceTO.getPropagationActions().add(LDAPPasswordPropagationActions.class.getSimpleName());
1053         resourceTO.getPropagationActions().remove(GenerateRandomPasswordPropagationActions.class.getSimpleName());
1054         RESOURCE_SERVICE.update(resourceTO);
1055 
1056         // 3. Add a resource to the User
1057         UserUR userUR = new UserUR();
1058         userUR.setKey(user.getKey());
1059         userUR.getResources().add(new StringPatchItem.Builder().
1060                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_LDAP).build());
1061 
1062         userUR.setPassword(new PasswordPatch.Builder().onSyncope(false).resource(RESOURCE_NAME_LDAP).build());
1063 
1064         user = updateUser(userUR).getEntity();
1065         assertNotNull(user);
1066         assertEquals(1, user.getResources().size());
1067 
1068         // 4. Check that the LDAP resource has the correct password
1069         ConnObject connObject =
1070                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
1071 
1072         assertNotNull(getLdapRemoteObject(
1073                 connObject.getAttr(Name.NAME).get().getValues().get(0),
1074                 "security123",
1075                 connObject.getAttr(Name.NAME).get().getValues().get(0)));
1076 
1077         // 5. Remove LDAPPasswordPropagationActions
1078         resourceTO = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP);
1079         assertNotNull(resourceTO);
1080         resourceTO.getPropagationActions().remove(LDAPPasswordPropagationActions.class.getSimpleName());
1081         resourceTO.getPropagationActions().add(0, GenerateRandomPasswordPropagationActions.class.getSimpleName());
1082         RESOURCE_SERVICE.update(resourceTO);
1083     }
1084 
1085     @Test
1086     public void issueSYNCOPE391() {
1087         assumeFalse(IS_EXT_SEARCH_ENABLED);
1088 
1089         // 1. create user on Syncope with null password
1090         UserCR userCR = UserITCase.getUniqueSample("syncope391@syncope.apache.org");
1091         userCR.setPassword(null);
1092         userCR.setStorePassword(false);
1093 
1094         UserTO userTO = createUser(userCR).getEntity();
1095         assertNotNull(userTO);
1096         assertNull(userTO.getPassword());
1097 
1098         // 2. create existing user on csv and check that password on Syncope is null and that password on resource
1099         // doesn't change
1100         userCR = new UserCR();
1101         userCR.setRealm(SyncopeConstants.ROOT_REALM);
1102         userCR.setPassword(null);
1103         userCR.setStorePassword(false);
1104         userCR.setUsername("syncope391@syncope.apache.org");
1105         userCR.getPlainAttrs().add(attr("fullname", "fullname"));
1106         userCR.getPlainAttrs().add(attr("firstname", "nome0"));
1107         userCR.getPlainAttrs().add(attr("surname", "cognome0"));
1108         userCR.getPlainAttrs().add(attr("userId", "syncope391@syncope.apache.org"));
1109         userCR.getPlainAttrs().add(attr("email", "syncope391@syncope.apache.org"));
1110         userCR.getAuxClasses().add("csv");
1111         userCR.getResources().add(RESOURCE_NAME_CSV);
1112 
1113         userTO = createUser(userCR).getEntity();
1114         assertNotNull(userTO);
1115 
1116         ConnObject connObjectTO =
1117                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
1118         assertNotNull(connObjectTO);
1119 
1120         // check if password has not changed
1121         assertEquals("password0", connObjectTO.getAttr(OperationalAttributes.PASSWORD_NAME).get().getValues().get(0));
1122         assertNull(userTO.getPassword());
1123 
1124         // 3. create user with not null password and propagate onto resource-csv, specify not to save password on
1125         // Syncope local storage
1126         userCR = UserITCase.getUniqueSample("syncope391@syncope.apache.org");
1127         userCR.setPassword("passwordTESTNULL1");
1128         userCR.setStorePassword(false);
1129         userCR.getVirAttrs().clear();
1130         userCR.getAuxClasses().add("csv");
1131         userCR.getResources().add(RESOURCE_NAME_CSV);
1132 
1133         userTO = createUser(userCR).getEntity();
1134         assertNotNull(userTO);
1135 
1136         connObjectTO =
1137                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
1138         assertNotNull(connObjectTO);
1139 
1140         // check if password has been propagated and that saved userTO's password is null
1141         assertEquals(
1142                 "passwordTESTNULL1",
1143                 connObjectTO.getAttr(OperationalAttributes.PASSWORD_NAME).get().getValues().get(0));
1144         assertNull(userTO.getPassword());
1145 
1146         // 4. create user and propagate password on resource-csv and on Syncope local storage
1147         userCR = UserITCase.getUniqueSample("syncope391@syncope.apache.org");
1148         userCR.setPassword("passwordTESTNULL1");
1149         userCR.getVirAttrs().clear();
1150         userCR.getAuxClasses().add("csv");
1151         userCR.getResources().add(RESOURCE_NAME_CSV);
1152 
1153         // storePassword true by default
1154         userTO = createUser(userCR).getEntity();
1155         assertNotNull(userTO);
1156 
1157         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
1158         assertNotNull(connObjectTO);
1159 
1160         // check if password has been correctly propagated on Syncope and resource-csv as usual
1161         assertEquals(
1162                 "passwordTESTNULL1",
1163                 connObjectTO.getAttr(OperationalAttributes.PASSWORD_NAME).get().getValues().get(0));
1164         Triple<Map<String, Set<String>>, List<String>, UserTO> self =
1165                 CLIENT_FACTORY.create(userTO.getUsername(), "passwordTESTNULL1").self();
1166         assertNotNull(self);
1167 
1168         // 4. add password policy to resource with passwordNotStore to false --> must store password
1169         ResourceTO csv = RESOURCE_SERVICE.read(RESOURCE_NAME_CSV);
1170         assertNotNull(csv);
1171         try {
1172             csv.setPasswordPolicy("55e5de0b-c79c-4e66-adda-251b6fb8579a");
1173             RESOURCE_SERVICE.update(csv);
1174             csv = RESOURCE_SERVICE.read(RESOURCE_NAME_CSV);
1175             assertEquals("55e5de0b-c79c-4e66-adda-251b6fb8579a", csv.getPasswordPolicy());
1176 
1177             userCR = UserITCase.getUniqueSample("syncope391@syncope.apache.org");
1178             userCR.setPassword(null);
1179             userCR.setStorePassword(false);
1180             userCR.getVirAttrs().clear();
1181             userCR.getAuxClasses().add("csv");
1182             userCR.getResources().add(RESOURCE_NAME_CSV);
1183 
1184             createUser(userCR);
1185             fail("This should not happen");
1186         } catch (SyncopeClientException e) {
1187             assertEquals(ClientExceptionType.InvalidUser, e.getType());
1188             assertTrue(e.getMessage().contains("Password mandatory"));
1189         } finally {
1190             // resource csv with null password policy
1191             csv.setPasswordPolicy(null);
1192             RESOURCE_SERVICE.update(csv);
1193         }
1194     }
1195 
1196     @Test
1197     public void issueSYNCOPE647() {
1198         UserCR userCR = UserITCase.getUniqueSample("syncope647@syncope.apache.org");
1199         userCR.getResources().clear();
1200         userCR.getMemberships().clear();
1201         userCR.getVirAttrs().clear();
1202         userCR.getAuxClasses().add("csv");
1203 
1204         userCR.getAuxClasses().add("generic membership");
1205         userCR.getPlainAttrs().add(attr("postalAddress", "postalAddress"));
1206 
1207         userCR.getResources().add(RESOURCE_NAME_LDAP);
1208 
1209         UserTO actual = createUser(userCR).getEntity();
1210         assertNotNull(actual);
1211         assertNotNull(actual.getDerAttr("csvuserid"));
1212 
1213         ConnObject connObjectTO =
1214                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), actual.getKey());
1215         assertNotNull(connObjectTO);
1216         assertEquals("postalAddress", connObjectTO.getAttr("postalAddress").get().getValues().get(0));
1217 
1218         UserUR userUR = new UserUR();
1219         userUR.setKey(actual.getKey());
1220         userUR.getPlainAttrs().add(attrAddReplacePatch("postalAddress", "newPostalAddress"));
1221 
1222         actual = updateUser(userUR).getEntity();
1223 
1224         connObjectTO = RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), actual.getKey());
1225         assertNotNull(connObjectTO);
1226         assertEquals("newPostalAddress", connObjectTO.getAttr("postalAddress").get().getValues().get(0));
1227     }
1228 
1229     @Test
1230     public void issueSYNCOPE626() {
1231         DefaultPasswordRuleConf ruleConf = new DefaultPasswordRuleConf();
1232         ruleConf.setUsernameAllowed(false);
1233 
1234         ImplementationTO rule = new ImplementationTO();
1235         rule.setKey("DefaultPasswordRuleConf" + getUUIDString());
1236         rule.setEngine(ImplementationEngine.JAVA);
1237         rule.setType(IdRepoImplementationType.PASSWORD_RULE);
1238         rule.setBody(POJOHelper.serialize(ruleConf));
1239         Response response = IMPLEMENTATION_SERVICE.create(rule);
1240         rule.setKey(response.getHeaderString(RESTHeaders.RESOURCE_KEY));
1241 
1242         PasswordPolicyTO passwordPolicy = new PasswordPolicyTO();
1243         passwordPolicy.setName("Password Policy for SYNCOPE-626");
1244         passwordPolicy.getRules().add(rule.getKey());
1245 
1246         passwordPolicy = createPolicy(PolicyType.PASSWORD, passwordPolicy);
1247         assertNotNull(passwordPolicy);
1248 
1249         RealmTO realm = REALM_SERVICE.search(new RealmQuery.Builder().keyword("two").build()).getResult().get(0);
1250         String oldPasswordPolicy = realm.getPasswordPolicy();
1251         realm.setPasswordPolicy(passwordPolicy.getKey());
1252         REALM_SERVICE.update(realm);
1253 
1254         try {
1255             UserCR userCR = UserITCase.getUniqueSample("syncope626@syncope.apache.org");
1256             userCR.setRealm(realm.getFullPath());
1257             userCR.setPassword(userCR.getUsername());
1258             try {
1259                 createUser(userCR);
1260                 fail("This should not happen");
1261             } catch (SyncopeClientException e) {
1262                 assertEquals(ClientExceptionType.InvalidUser, e.getType());
1263                 assertTrue(e.getElements().iterator().next().startsWith("InvalidPassword"));
1264             }
1265 
1266             userCR.setPassword("password123");
1267             UserTO user = createUser(userCR).getEntity();
1268             assertNotNull(user);
1269         } finally {
1270             realm.setPasswordPolicy(oldPasswordPolicy);
1271             REALM_SERVICE.update(realm);
1272 
1273             POLICY_SERVICE.delete(PolicyType.PASSWORD, passwordPolicy.getKey());
1274         }
1275 
1276     }
1277 
1278     @Test
1279     public void issueSYNCOPE686() {
1280         // 1. read configured cipher algorithm in order to be able to restore it at the end of test
1281         String origpwdCipherAlgo = confParamOps.get(SyncopeConstants.MASTER_DOMAIN,
1282                 "password.cipher.algorithm", null, String.class);
1283 
1284         // 2. set AES password cipher algorithm
1285         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", "AES");
1286 
1287         try {
1288             // 3. create group with LDAP resource assigned
1289             GroupCR groupCR = GroupITCase.getBasicSample("syncope686");
1290             groupCR.getResources().add(RESOURCE_NAME_LDAP);
1291             GroupTO group = createGroup(groupCR).getEntity();
1292             assertNotNull(group);
1293 
1294             // 4. create user with no resources
1295             UserCR userCR = UserITCase.getUniqueSample("syncope686@apache.org");
1296             userCR.getResources().clear();
1297 
1298             UserTO userTO = createUser(userCR).getEntity();
1299             assertNotNull(userTO);
1300 
1301             // 5. update user with the new group, and don't provide any password
1302             UserUR userUR = new UserUR();
1303             userUR.setKey(userTO.getKey());
1304             userUR.getMemberships().add(new MembershipUR.Builder(group.getKey()).
1305                     operation(PatchOperation.ADD_REPLACE).build());
1306 
1307             ProvisioningResult<UserTO> result = updateUser(userUR);
1308             assertNotNull(result);
1309 
1310             // 5. verify that propagation was successful
1311             List<PropagationStatus> props = result.getPropagationStatuses();
1312             assertNotNull(props);
1313             assertEquals(1, props.size());
1314             PropagationStatus prop = props.get(0);
1315             assertNotNull(prop);
1316             assertEquals(RESOURCE_NAME_LDAP, prop.getResource());
1317             assertEquals(ExecStatus.SUCCESS, prop.getStatus());
1318         } finally {
1319             // restore initial cipher algorithm
1320             confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", origpwdCipherAlgo);
1321         }
1322     }
1323 
1324     @Test
1325     public void issueSYNCOPE710() {
1326         // 1. create groups for indirect resource assignment
1327         GroupCR ldapGroupCR = GroupITCase.getBasicSample("syncope710.ldap");
1328         ldapGroupCR.getResources().add(RESOURCE_NAME_LDAP);
1329         GroupTO ldapGroup = createGroup(ldapGroupCR).getEntity();
1330 
1331         GroupCR dbGroupCR = GroupITCase.getBasicSample("syncope710.db");
1332         dbGroupCR.getResources().add(RESOURCE_NAME_TESTDB);
1333         GroupTO dbGroup = createGroup(dbGroupCR).getEntity();
1334 
1335         // 2. create user with memberships for the groups created above
1336         UserCR userCR = UserITCase.getUniqueSample("syncope710@syncope.apache.org");
1337         userCR.getResources().clear();
1338         userCR.getMemberships().clear();
1339         userCR.getMemberships().add(new MembershipTO.Builder(ldapGroup.getKey()).build());
1340         userCR.getMemberships().add(new MembershipTO.Builder(dbGroup.getKey()).build());
1341 
1342         ProvisioningResult<UserTO> result = createUser(userCR);
1343         assertEquals(2, result.getPropagationStatuses().size());
1344         UserTO userTO = result.getEntity();
1345 
1346         // 3. request to propagate password only to db
1347         UserUR userUR = new UserUR();
1348         userUR.setKey(userTO.getKey());
1349         userUR.setPassword(new PasswordPatch.Builder().
1350                 onSyncope(false).resource(RESOURCE_NAME_TESTDB).value("newpassword123").build());
1351 
1352         result = updateUser(userUR);
1353         assertEquals(1, result.getPropagationStatuses().size());
1354         assertEquals(RESOURCE_NAME_TESTDB, result.getPropagationStatuses().get(0).getResource());
1355     }
1356 
1357     @Test
1358     public void issueSYNCOPE881() {
1359         // 1. create group and assign LDAP
1360         GroupCR groupCR = GroupITCase.getSample("syncope881G");
1361         groupCR.getVirAttrs().add(attr("rvirtualdata", "rvirtualvalue"));
1362 
1363         GroupTO group = createGroup(groupCR).getEntity();
1364         assertNotNull(group);
1365         assertNotNull(RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), group.getKey()));
1366 
1367         // 2. create user and assign such group
1368         UserCR userCR = UserITCase.getUniqueSample("syncope881U@apache.org");
1369         userCR.getMemberships().clear();
1370         userCR.getMemberships().add(new MembershipTO.Builder(group.getKey()).build());
1371 
1372         UserTO user = createUser(userCR).getEntity();
1373         assertNotNull(user);
1374 
1375         // 3. verify that user is in LDAP
1376         ConnObject connObject =
1377                 RESOURCE_SERVICE.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
1378         assertNotNull(connObject);
1379         Attr userDn = connObject.getAttr(Name.NAME).get();
1380         assertNotNull(userDn);
1381         assertEquals(1, userDn.getValues().size());
1382         assertNotNull(getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, userDn.getValues().get(0)));
1383 
1384         // 4. remove user
1385         USER_SERVICE.delete(user.getKey());
1386 
1387         // 5. verify that user is not in LDAP anymore
1388         assertNull(getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, userDn.getValues().get(0)));
1389     }
1390 
1391     @Test
1392     public void issueSYNCOPE1099() {
1393         // 1. create group with dynamic condition and resource
1394         GroupCR groupCR = GroupITCase.getSample("syncope1099G");
1395         groupCR.getResources().clear();
1396         groupCR.getResources().add(RESOURCE_NAME_TESTDB);
1397         groupCR.setUDynMembershipCond("firstname==issueSYNCOPE1099");
1398 
1399         GroupTO group = createGroup(groupCR).getEntity();
1400         assertNotNull(group);
1401 
1402         // 2. create user matching the condition above
1403         UserCR userCR = UserITCase.getUniqueSample("syncope1099U@apache.org");
1404         userCR.getPlainAttr("firstname").get().getValues().set(0, "issueSYNCOPE1099");
1405 
1406         ProvisioningResult<UserTO> created = createUser(userCR);
1407         assertNotNull(created);
1408 
1409         // 3. verify that dynamic membership is set and that resource is consequently assigned
1410         UserTO user = created.getEntity();
1411         String groupKey = group.getKey();
1412         assertTrue(user.getDynMemberships().stream().anyMatch(m -> m.getGroupKey().equals(groupKey)));
1413         assertTrue(user.getResources().contains(RESOURCE_NAME_TESTDB));
1414 
1415         // 4. verify that propagation happened towards the resource of the dynamic group
1416         assertFalse(created.getPropagationStatuses().isEmpty());
1417         assertEquals(RESOURCE_NAME_TESTDB, created.getPropagationStatuses().get(0).getResource());
1418     }
1419 
1420     @Test
1421     public void issueSYNCOPE1166() {
1422         UserCR userCR = UserITCase.getUniqueSample("syncope1166@apache.org");
1423         UserTO userTO = createUser(userCR).getEntity();
1424         assertNotNull(userTO);
1425 
1426         UserUR userUR = new UserUR();
1427         userUR.setKey(userTO.getKey());
1428         // resource-ldap has password mapped, resource-db-virattr does not
1429         userUR.setPassword(new PasswordPatch.Builder().
1430                 onSyncope(true).
1431                 resource(RESOURCE_NAME_LDAP).
1432                 value("new2Password").build());
1433 
1434         userUR.getResources().add(new StringPatchItem.Builder().
1435                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_LDAP).build());
1436         userUR.getResources().add(new StringPatchItem.Builder().
1437                 operation(PatchOperation.ADD_REPLACE).value(RESOURCE_NAME_DBVIRATTR).build());
1438 
1439         ProvisioningResult<UserTO> result = updateUser(userUR);
1440         assertNotNull(result);
1441         assertEquals(2, result.getPropagationStatuses().size());
1442         assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
1443         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus());
1444         assertEquals(RESOURCE_NAME_DBVIRATTR, result.getPropagationStatuses().get(1).getResource());
1445         assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(1).getStatus());
1446     }
1447 
1448     @Test
1449     public void issueSYNCOPE1206() {
1450         // 1. create group with dynamic user condition 'cool==true'
1451         GroupCR dynGroupCR = GroupITCase.getSample("syncope1206");
1452         dynGroupCR.setUDynMembershipCond(
1453                 SyncopeClient.getUserSearchConditionBuilder().is("cool").equalTo("true").query());
1454         GroupTO dynGroup = createGroup(dynGroupCR).getEntity();
1455         assertNotNull(dynGroup);
1456         assertTrue(dynGroup.getResources().contains(RESOURCE_NAME_LDAP));
1457 
1458         // 2. create user (no value for cool, no dynamic membership, no propagation to LDAP)
1459         UserCR userCR = UserITCase.getUniqueSample("syncope1206@apache.org");
1460         userCR.getResources().clear();
1461 
1462         ProvisioningResult<UserTO> result = createUser(userCR);
1463         assertTrue(result.getPropagationStatuses().isEmpty());
1464 
1465         // 3. update user to match the dynamic condition: expect propagation to LDAP
1466         UserUR userUR = new UserUR();
1467         userUR.setKey(result.getEntity().getKey());
1468         userUR.getPlainAttrs().add(new AttrPatch.Builder(attr("cool", "true")).build());
1469 
1470         result = updateUser(userUR);
1471         assertEquals(1, result.getPropagationStatuses().size());
1472         assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
1473 
1474         // 4. update again user to not match the dynamic condition any more: expect propagation to LDAP
1475         userUR = new UserUR();
1476         userUR.setKey(result.getEntity().getKey());
1477         userUR.getPlainAttrs().add(new AttrPatch.Builder(attr("cool", "false")).build());
1478 
1479         result = updateUser(userUR);
1480         assertEquals(1, result.getPropagationStatuses().size());
1481         assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource());
1482     }
1483 
1484     @Test
1485     public void issueSYNCOPE1337() {
1486         // 1. save current cipher algorithm and set it to something salted
1487         String original = confParamOps.get(SyncopeConstants.MASTER_DOMAIN,
1488                 "password.cipher.algorithm", null, String.class);
1489 
1490         confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", CipherAlgorithm.SSHA512.name());
1491 
1492         try {
1493             // 2. create user under /even/two to get password policy with history length 1
1494             UserCR userCR = UserITCase.getUniqueSample("syncope1337@apache.org");
1495             userCR.setPassword("Password123");
1496             userCR.setRealm("/even/two");
1497             UserTO userTO = createUser(userCR).getEntity();
1498             assertNotNull(userTO);
1499 
1500             // 3. attempt to set the same password value: fails
1501             UserUR req = new UserUR();
1502             req.setKey(userTO.getKey());
1503             req.setPassword(new PasswordPatch.Builder().onSyncope(true).value("Password123").build());
1504             try {
1505                 updateUser(req);
1506                 fail("Password update should not work");
1507             } catch (SyncopeClientException e) {
1508                 assertEquals(ClientExceptionType.InvalidUser, e.getType());
1509                 assertTrue(e.getMessage().contains("InvalidPassword"));
1510             }
1511 
1512             // 4. set another password value: works
1513             req.setPassword(new PasswordPatch.Builder().onSyncope(true).value("Password124").build());
1514             userTO = updateUser(req).getEntity();
1515             assertNotNull(userTO);
1516 
1517             // 5. set the original password value: works (history length is 1)
1518             req.setPassword(new PasswordPatch.Builder().onSyncope(true).value("Password123").build());
1519             userTO = updateUser(req).getEntity();
1520             assertNotNull(userTO);
1521         } finally {
1522             // finally revert the cipher algorithm
1523             confParamOps.set(SyncopeConstants.MASTER_DOMAIN, "password.cipher.algorithm", original);
1524         }
1525     }
1526 
1527     @Test
1528     public void issueSYNCOPE1472() {
1529         // 1. update user rossini by assigning twice resource-testdb2 and auxiliary class csv
1530         UserUR userUR = new UserUR();
1531         userUR.setKey("1417acbe-cbf6-4277-9372-e75e04f97000");
1532         userUR.setPassword(new PasswordPatch.Builder()
1533                 .onSyncope(false)
1534                 .resource(RESOURCE_NAME_TESTDB)
1535                 .value("Password123")
1536                 .build());
1537         userUR.getResources().add(new StringPatchItem.Builder()
1538                 .value(RESOURCE_NAME_TESTDB)
1539                 .operation(PatchOperation.ADD_REPLACE)
1540                 .build());
1541         userUR.getAuxClasses().add(new StringPatchItem.Builder()
1542                 .operation(PatchOperation.ADD_REPLACE)
1543                 .value("csv")
1544                 .build());
1545         userUR.getRoles().add(new StringPatchItem.Builder()
1546                 .operation(PatchOperation.ADD_REPLACE)
1547                 .value("Other")
1548                 .build());
1549 
1550         updateUser(userUR);
1551         updateUser(userUR);
1552 
1553         // 2. remove resources, auxiliary classes and roles
1554         userUR.getResources().clear();
1555         userUR.getResources().add(new StringPatchItem.Builder()
1556                 .value(RESOURCE_NAME_TESTDB)
1557                 .operation(PatchOperation.DELETE)
1558                 .build());
1559         userUR.getAuxClasses().clear();
1560         userUR.getAuxClasses().add(new StringPatchItem.Builder()
1561                 .value("csv")
1562                 .operation(PatchOperation.DELETE)
1563                 .build());
1564         userUR.getRoles().clear();
1565         userUR.getRoles().add(new StringPatchItem.Builder()
1566                 .value("Other")
1567                 .operation(PatchOperation.DELETE)
1568                 .build());
1569         updateUser(userUR);
1570 
1571         UserTO userTO = USER_SERVICE.read("1417acbe-cbf6-4277-9372-e75e04f97000");
1572         assertFalse(userTO.getResources().contains(RESOURCE_NAME_TESTDB), "Should not contain removed resources");
1573         assertFalse(userTO.getAuxClasses().contains("csv"), "Should not contain removed auxiliary classes");
1574         assertFalse(userTO.getRoles().contains("Other"), "Should not contain removed roles");
1575     }
1576 
1577     @Test
1578     public void issueSYNCOPE1699() throws Exception {
1579         UserTO userTO = createUser(UserITCase.getUniqueSample("syncope1669@apache.org")).getEntity();
1580 
1581         UserUR req = new UserUR();
1582         req.setUsername(new StringReplacePatchItem.Builder().value("newUsername" + getUUIDString()).build());
1583 
1584         WebClient webClient = WebClient.create(ADDRESS + "/users/" + userTO.getKey(), ADMIN_UNAME, ADMIN_PWD, null).
1585                 accept(MediaType.APPLICATION_JSON_TYPE).
1586                 type(MediaType.APPLICATION_JSON_TYPE);
1587 
1588         Response response = webClient.invoke(HttpMethod.PATCH, JSON_MAPPER.writeValueAsString(req));
1589         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
1590 
1591         // Key is mismatched in the path parameter and the request body.
1592         req.setKey(UUID.randomUUID().toString());
1593         response = webClient.invoke(HttpMethod.PATCH, JSON_MAPPER.writeValueAsString(req));
1594         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
1595 
1596         // reading user by its username still works
1597         userTO = USER_SERVICE.read(req.getUsername().getValue());
1598         assertNotNull(userTO);
1599     }
1600 
1601     @Test
1602     public void issueSYNCOPE1750() {
1603         UserCR userCR = UserITCase.getUniqueSample("syncope1750@apache.org");
1604         userCR.getResources().add(RESOURCE_NAME_NOPROPAGATION);
1605         UserTO userTO = createUser(userCR).getEntity();
1606 
1607         UserUR req = new UserUR.Builder(userTO.getKey()).password(new PasswordPatch.Builder().
1608                 onSyncope(false).resource(RESOURCE_NAME_NOPROPAGATION).value("short").build()).build();
1609 
1610         try {
1611             USER_SERVICE.update(req);
1612             fail();
1613         } catch (SyncopeClientException e) {
1614             assertEquals(ClientExceptionType.InvalidUser, e.getType());
1615             assertTrue(e.getMessage().contains("InvalidPassword: Password must be 10 or more characters in length."));
1616         }
1617     }
1618 
1619     @Test
1620     public void issueSYNCOPE1793() {
1621         RoleTO role = new RoleTO();
1622         role.setKey("syncope1793" + getUUIDString());
1623         role.getRealms().add(SyncopeConstants.ROOT_REALM);
1624         role.getEntitlements().add(IdRepoEntitlement.USER_UPDATE);
1625         role = createRole(role);
1626 
1627         UserCR userCR = UserITCase.getUniqueSample("syncope1793@apache.org");
1628         userCR.getRoles().add(role.getKey());
1629         UserTO userTO = createUser(userCR).getEntity();
1630 
1631         UserService userService = CLIENT_FACTORY.create(userTO.getUsername(), "password123").
1632                 getService(UserService.class);
1633         Response response = userService.associate(new ResourceAR.Builder().key(userTO.getKey()).
1634                 resource(RESOURCE_NAME_NOPROPAGATION).action(ResourceAssociationAction.ASSIGN).build());
1635         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
1636     }
1637 }