View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.fit.core;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
26  import static org.junit.jupiter.api.Assumptions.assumeTrue;
27  
28  import java.security.AccessControlException;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.stream.Collectors;
33  import javax.ws.rs.ForbiddenException;
34  import javax.ws.rs.core.GenericType;
35  import javax.ws.rs.core.Response;
36  import org.apache.commons.lang3.tuple.Triple;
37  import org.apache.syncope.client.lib.BasicAuthenticationHandler;
38  import org.apache.syncope.client.lib.SyncopeClient;
39  import org.apache.syncope.common.lib.SyncopeClientException;
40  import org.apache.syncope.common.lib.SyncopeConstants;
41  import org.apache.syncope.common.lib.request.AnyObjectCR;
42  import org.apache.syncope.common.lib.request.GroupCR;
43  import org.apache.syncope.common.lib.request.MembershipUR;
44  import org.apache.syncope.common.lib.request.PasswordPatch;
45  import org.apache.syncope.common.lib.request.ResourceDR;
46  import org.apache.syncope.common.lib.request.StatusR;
47  import org.apache.syncope.common.lib.request.StringPatchItem;
48  import org.apache.syncope.common.lib.request.StringReplacePatchItem;
49  import org.apache.syncope.common.lib.request.UserCR;
50  import org.apache.syncope.common.lib.request.UserUR;
51  import org.apache.syncope.common.lib.to.AnyTO;
52  import org.apache.syncope.common.lib.to.AnyTypeClassTO;
53  import org.apache.syncope.common.lib.to.AnyTypeTO;
54  import org.apache.syncope.common.lib.to.GroupTO;
55  import org.apache.syncope.common.lib.to.MembershipTO;
56  import org.apache.syncope.common.lib.to.PagedResult;
57  import org.apache.syncope.common.lib.to.PlainSchemaTO;
58  import org.apache.syncope.common.lib.to.ProvisioningResult;
59  import org.apache.syncope.common.lib.to.RoleTO;
60  import org.apache.syncope.common.lib.to.UserRequestForm;
61  import org.apache.syncope.common.lib.to.UserTO;
62  import org.apache.syncope.common.lib.types.AnyTypeKind;
63  import org.apache.syncope.common.lib.types.AttrSchemaType;
64  import org.apache.syncope.common.lib.types.CipherAlgorithm;
65  import org.apache.syncope.common.lib.types.ClientExceptionType;
66  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
67  import org.apache.syncope.common.lib.types.PatchOperation;
68  import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
69  import org.apache.syncope.common.lib.types.SchemaType;
70  import org.apache.syncope.common.lib.types.StatusRType;
71  import org.apache.syncope.common.rest.api.RESTHeaders;
72  import org.apache.syncope.common.rest.api.beans.AnyQuery;
73  import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
74  import org.apache.syncope.common.rest.api.service.AnyObjectService;
75  import org.apache.syncope.common.rest.api.service.SchemaService;
76  import org.apache.syncope.common.rest.api.service.UserService;
77  import org.apache.syncope.core.spring.security.Encryptor;
78  import org.apache.syncope.fit.AbstractITCase;
79  import org.junit.jupiter.api.Test;
80  import org.springframework.jdbc.core.JdbcTemplate;
81  
82  public class AuthenticationITCase extends AbstractITCase {
83  
84      private static int getFailedLogins(final UserService userService, final String userKey) {
85          UserTO readUserTO = userService.read(userKey);
86          assertNotNull(readUserTO);
87          assertNotNull(readUserTO.getFailedLogins());
88          return readUserTO.getFailedLogins();
89      }
90  
91      @Test
92      public void readEntitlements() {
93          // 1. as anonymous
94          Triple<Map<String, Set<String>>, List<String>, UserTO> self = ANONYMOUS_CLIENT.self();
95          assertEquals(1, self.getLeft().size());
96          assertTrue(self.getLeft().keySet().contains(IdRepoEntitlement.ANONYMOUS));
97          assertEquals(List.of(), self.getMiddle());
98          assertEquals(ANONYMOUS_UNAME, self.getRight().getUsername());
99  
100         // 3. as admin
101         self = ADMIN_CLIENT.self();
102         assertEquals(ANONYMOUS_CLIENT.platform().getEntitlements().size(), self.getLeft().size());
103         assertFalse(self.getLeft().keySet().contains(IdRepoEntitlement.ANONYMOUS));
104         assertEquals(List.of(), self.getMiddle());
105         assertEquals(ADMIN_UNAME, self.getRight().getUsername());
106 
107         // 4. as user
108         self = CLIENT_FACTORY.create("bellini", ADMIN_PWD).self();
109         assertFalse(self.getLeft().isEmpty());
110         assertFalse(self.getLeft().keySet().contains(IdRepoEntitlement.ANONYMOUS));
111         assertEquals(List.of(), self.getMiddle());
112         assertEquals("bellini", self.getRight().getUsername());
113     }
114 
115     @Test
116     public void userSchemaAuthorization() {
117         String schemaName = "authTestSchema" + getUUIDString();
118 
119         // 1. create a schema (as admin)
120         PlainSchemaTO schemaTO = new PlainSchemaTO();
121         schemaTO.setKey(schemaName);
122         schemaTO.setMandatoryCondition("false");
123         schemaTO.setType(AttrSchemaType.String);
124 
125         PlainSchemaTO newPlainSchemaTO = createSchema(SchemaType.PLAIN, schemaTO);
126         assertEquals(schemaTO, newPlainSchemaTO);
127 
128         // 2. create an user with the role created above (as admin)
129         UserCR userCR = UserITCase.getUniqueSample("auth@test.org");
130         UserTO userTO = createUser(userCR).getEntity();
131         assertNotNull(userTO);
132 
133         // 3. read the schema created above (as admin) - success
134         schemaTO = SCHEMA_SERVICE.read(SchemaType.PLAIN, schemaName);
135         assertNotNull(schemaTO);
136 
137         // 4. read the schema created above (as user) - success
138         SchemaService schemaService2 = CLIENT_FACTORY.create(userTO.getUsername(), "password123").
139                 getService(SchemaService.class);
140         schemaTO = schemaService2.read(SchemaType.PLAIN, schemaName);
141         assertNotNull(schemaTO);
142 
143         // 5. update the schema create above (as user) - failure
144         try {
145             schemaService2.update(SchemaType.PLAIN, schemaTO);
146             fail("Schema update as user should not work");
147         } catch (ForbiddenException e) {
148             assertNotNull(e);
149         }
150 
151         assertEquals(0, getFailedLogins(USER_SERVICE, userTO.getKey()));
152     }
153 
154     @Test
155     public void userRead() {
156         UserCR userCR = UserITCase.getUniqueSample("testuserread@test.org");
157         userCR.getRoles().add("User manager");
158 
159         UserTO userTO = createUser(userCR).getEntity();
160         assertNotNull(userTO);
161 
162         UserService userService2 = CLIENT_FACTORY.create(userTO.getUsername(), "password123").
163                 getService(UserService.class);
164 
165         UserTO readUserTO = userService2.read("1417acbe-cbf6-4277-9372-e75e04f97000");
166         assertNotNull(readUserTO);
167 
168         UserService userService3 = CLIENT_FACTORY.create("puccini", ADMIN_PWD).getService(UserService.class);
169 
170         try {
171             userService3.read("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee");
172             fail("This should not happen");
173         } catch (SyncopeClientException e) {
174             assertNotNull(e);
175             assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
176         }
177     }
178 
179     @Test
180     public void userSearch() {
181         UserCR userCR = UserITCase.getUniqueSample("testusersearch@test.org");
182         userCR.getRoles().add("User reviewer");
183 
184         UserTO userTO = createUser(userCR).getEntity();
185         assertNotNull(userTO);
186 
187         // 1. user assigned to role 1, with search entitlement on realms /odd and /even: won't find anything with 
188         // root realm
189         UserService userService2 = CLIENT_FACTORY.create(userTO.getUsername(), "password123").
190                 getService(UserService.class);
191 
192         if (IS_EXT_SEARCH_ENABLED) {
193             try {
194                 Thread.sleep(2000);
195             } catch (InterruptedException ex) {
196                 // ignore
197             }
198         }
199 
200         PagedResult<UserTO> matchingUsers = userService2.search(new AnyQuery.Builder().
201                 realm(SyncopeConstants.ROOT_REALM).
202                 fiql(SyncopeClient.getUserSearchConditionBuilder().isNotNull("key").query()).build());
203         assertNotNull(matchingUsers);
204         assertFalse(matchingUsers.getResult().isEmpty());
205         Set<String> matchingUserKeys = matchingUsers.getResult().stream().
206                 map(AnyTO::getKey).collect(Collectors.toSet());
207         assertTrue(matchingUserKeys.contains("1417acbe-cbf6-4277-9372-e75e04f97000"));
208         assertFalse(matchingUserKeys.contains("74cd8ece-715a-44a4-a736-e17b46c4e7e6"));
209         assertFalse(matchingUserKeys.contains("823074dc-d280-436d-a7dd-07399fae48ec"));
210 
211         // 2. user assigned to role 4, with search entitlement on realm /even/two
212         UserService userService3 = CLIENT_FACTORY.create("puccini", ADMIN_PWD).getService(UserService.class);
213 
214         matchingUsers = userService3.search(new AnyQuery.Builder().realm("/even/two").
215                 fiql(SyncopeClient.getUserSearchConditionBuilder().isNotNull("loginDate").query()).build());
216         assertNotNull(matchingUsers);
217         assertTrue(matchingUsers.getResult().stream().allMatch(matching -> "/even/two".equals(matching.getRealm())));
218     }
219 
220     @Test
221     public void delegatedUserCRUD() {
222         String roleKey = null;
223         String delegatedAdminKey = null;
224         try {
225             // 1. create role for full user administration, under realm /even/two
226             RoleTO role = new RoleTO();
227             role.setKey("Delegated user admin");
228             role.getEntitlements().add(IdRepoEntitlement.USER_CREATE);
229             role.getEntitlements().add(IdRepoEntitlement.USER_UPDATE);
230             role.getEntitlements().add(IdRepoEntitlement.USER_DELETE);
231             role.getEntitlements().add(IdRepoEntitlement.USER_SEARCH);
232             role.getEntitlements().add(IdRepoEntitlement.USER_READ);
233             role.getRealms().add("/even/two");
234 
235             roleKey = ROLE_SERVICE.create(role).getHeaderString(RESTHeaders.RESOURCE_KEY);
236             assertNotNull(roleKey);
237 
238             // 2. as admin, create delegated admin user, and assign the role just created
239             UserCR delegatedAdminCR = UserITCase.getUniqueSample("admin@syncope.apache.org");
240             delegatedAdminCR.getRoles().add(roleKey);
241             UserTO delegatedAdmin = createUser(delegatedAdminCR).getEntity();
242             delegatedAdminKey = delegatedAdmin.getKey();
243 
244             // 3. instantiate a delegate user service client, for further operations
245             UserService delegatedUserService =
246                     CLIENT_FACTORY.create(delegatedAdmin.getUsername(), "password123").getService(UserService.class);
247 
248             // 4. as delegated, create user under realm / -> fail
249             UserCR userCR = UserITCase.getUniqueSample("delegated@syncope.apache.org");
250             try {
251                 delegatedUserService.create(userCR);
252                 fail("This should not happen");
253             } catch (SyncopeClientException e) {
254                 assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
255             }
256 
257             // 5. set realm to /even/two -> succeed
258             userCR.setRealm("/even/two");
259 
260             Response response = delegatedUserService.create(userCR);
261             assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
262 
263             UserTO user = response.readEntity(new GenericType<ProvisioningResult<UserTO>>() {
264             }).getEntity();
265             assertEquals("surname", user.getPlainAttr("surname").get().getValues().get(0));
266 
267             // 5. as delegated, update user attempting to move under realm / -> fail
268             UserUR userUR = new UserUR();
269             userUR.setKey(user.getKey());
270             userUR.setRealm(new StringReplacePatchItem.Builder().value("/odd").build());
271             userUR.getPlainAttrs().add(attrAddReplacePatch("surname", "surname2"));
272 
273             try {
274                 delegatedUserService.update(userUR);
275                 fail("This should not happen");
276             } catch (SyncopeClientException e) {
277                 assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
278             }
279 
280             // 6. revert realm change -> succeed
281             userUR.setRealm(null);
282 
283             response = delegatedUserService.update(userUR);
284             assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
285 
286             user = response.readEntity(new GenericType<ProvisioningResult<UserTO>>() {
287             }).getEntity();
288             assertEquals("surname2", user.getPlainAttr("surname").get().getValues().get(0));
289 
290             // 7. as delegated, delete user
291             delegatedUserService.delete(user.getKey());
292 
293             try {
294                 USER_SERVICE.read(user.getKey());
295                 fail("This should not happen");
296             } catch (SyncopeClientException e) {
297                 assertEquals(ClientExceptionType.NotFound, e.getType());
298             }
299         } finally {
300             if (roleKey != null) {
301                 ROLE_SERVICE.delete(roleKey);
302             }
303             if (delegatedAdminKey != null) {
304                 USER_SERVICE.delete(delegatedAdminKey);
305             }
306         }
307     }
308 
309     @Test
310     public void checkFailedLogins() {
311         UserCR userCR = UserITCase.getUniqueSample("checkFailedLogin@syncope.apache.org");
312         userCR.getRoles().add("User manager");
313 
314         UserTO userTO = createUser(userCR).getEntity();
315         assertNotNull(userTO);
316         String userKey = userTO.getKey();
317 
318         UserService userService2 = CLIENT_FACTORY.create(userTO.getUsername(), "password123").
319                 getService(UserService.class);
320         assertEquals(0, getFailedLogins(userService2, userKey));
321 
322         // authentications failed ...
323         try {
324             CLIENT_FACTORY.create(userTO.getUsername(), "wrongpwd1");
325             fail("This should not happen");
326         } catch (AccessControlException e) {
327             assertNotNull(e);
328         }
329         try {
330             CLIENT_FACTORY.create(userTO.getUsername(), "wrongpwd1");
331             fail("This should not happen");
332         } catch (AccessControlException e) {
333             assertNotNull(e);
334         }
335         assertEquals(2, getFailedLogins(USER_SERVICE, userKey));
336 
337         UserService userService4 = CLIENT_FACTORY.create(userTO.getUsername(), "password123").
338                 getService(UserService.class);
339         assertEquals(0, getFailedLogins(userService4, userKey));
340     }
341 
342     @Test
343     public void checkUserSuspension() {
344         UserCR userCR = UserITCase.getUniqueSample("checkSuspension@syncope.apache.org");
345         userCR.setRealm("/odd");
346         userCR.getRoles().add("User manager");
347 
348         UserTO userTO = createUser(userCR).getEntity();
349         String userKey = userTO.getKey();
350         assertNotNull(userTO);
351 
352         assertEquals(0, getFailedLogins(USER_SERVICE, userKey));
353 
354         // authentications failed ...
355         try {
356             CLIENT_FACTORY.create(userTO.getUsername(), "wrongpwd1");
357             fail("This should not happen");
358         } catch (AccessControlException e) {
359             assertNotNull(e);
360         }
361         try {
362             CLIENT_FACTORY.create(userTO.getUsername(), "wrongpwd1");
363             fail("This should not happen");
364         } catch (AccessControlException e) {
365             assertNotNull(e);
366         }
367         try {
368             CLIENT_FACTORY.create(userTO.getUsername(), "wrongpwd1");
369             fail("This should not happen");
370         } catch (AccessControlException e) {
371             assertNotNull(e);
372         }
373 
374         assertEquals(3, getFailedLogins(USER_SERVICE, userKey));
375 
376         // last authentication before suspension
377         try {
378             CLIENT_FACTORY.create(userTO.getUsername(), "wrongpwd1");
379             fail("This should not happen");
380         } catch (AccessControlException e) {
381             assertNotNull(e);
382         }
383 
384         userTO = USER_SERVICE.read(userTO.getKey());
385         assertNotNull(userTO);
386         assertNotNull(userTO.getFailedLogins());
387         assertEquals(3, userTO.getFailedLogins().intValue());
388         assertEquals("suspended", userTO.getStatus());
389 
390         // Access with correct credentials should fail as user is suspended
391         try {
392             CLIENT_FACTORY.create(userTO.getUsername(), "password123");
393             fail("This should not happen");
394         } catch (AccessControlException e) {
395             assertNotNull(e);
396         }
397 
398         StatusR reactivate = new StatusR.Builder(userTO.getKey(), StatusRType.REACTIVATE).build();
399         userTO = USER_SERVICE.status(reactivate).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
400         }).getEntity();
401         assertNotNull(userTO);
402         assertEquals("active", userTO.getStatus());
403 
404         SyncopeClient goodPwdClient = CLIENT_FACTORY.create(userTO.getUsername(), "password123");
405         assertEquals(0, goodPwdClient.self().getRight().getFailedLogins().intValue());
406     }
407 
408     @Test
409     public void anyTypeEntitlement() {
410         String anyTypeKey = "FOLDER " + getUUIDString();
411 
412         // 1. no entitlement exists (yet) for the any type to be created
413         assertFalse(ANONYMOUS_CLIENT.platform().getEntitlements().stream().
414                 anyMatch(entitlement -> entitlement.contains(anyTypeKey)));
415 
416         // 2. create plain schema, any type class and any type
417         PlainSchemaTO path = new PlainSchemaTO();
418         path.setKey("path" + getUUIDString());
419         path.setType(AttrSchemaType.String);
420         path = createSchema(SchemaType.PLAIN, path);
421 
422         AnyTypeClassTO anyTypeClass = new AnyTypeClassTO();
423         anyTypeClass.setKey("folder" + getUUIDString());
424         anyTypeClass.getPlainSchemas().add(path.getKey());
425         ANY_TYPE_CLASS_SERVICE.create(anyTypeClass);
426 
427         AnyTypeTO anyTypeTO = new AnyTypeTO();
428         anyTypeTO.setKey(anyTypeKey);
429         anyTypeTO.setKind(AnyTypeKind.ANY_OBJECT);
430         anyTypeTO.getClasses().add(anyTypeClass.getKey());
431         ANY_TYPE_SERVICE.create(anyTypeTO);
432 
433         // 2. now entitlement exists for the any type just created
434         assertTrue(ANONYMOUS_CLIENT.platform().getEntitlements().stream().
435                 anyMatch(entitlement -> entitlement.contains(anyTypeKey)));
436 
437         // 3. attempt to create an instance of the type above: fail because no entitlement was assigned
438         AnyObjectCR folder = new AnyObjectCR();
439         folder.setName("home");
440         folder.setRealm(SyncopeConstants.ROOT_REALM);
441         folder.setType(anyTypeKey);
442         folder.getPlainAttrs().add(attr(path.getKey(), "/home"));
443 
444         SyncopeClient belliniClient = CLIENT_FACTORY.create("bellini", ADMIN_PWD);
445         try {
446             belliniClient.getService(AnyObjectService.class).create(folder);
447             fail("This should not happen");
448         } catch (SyncopeClientException e) {
449             assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
450         }
451 
452         // 4. give create entitlement for the any type just created
453         RoleTO role = new RoleTO();
454         role.setKey("role" + getUUIDString());
455         role.getRealms().add(SyncopeConstants.ROOT_REALM);
456         role.getEntitlements().add(anyTypeKey + "_READ");
457         role.getEntitlements().add(anyTypeKey + "_CREATE");
458         role = createRole(role);
459 
460         UserTO bellini = USER_SERVICE.read("bellini");
461         UserUR req = new UserUR();
462         req.setKey(bellini.getKey());
463         req.getRoles().add(new StringPatchItem.Builder().
464                 operation(PatchOperation.ADD_REPLACE).value(role.getKey()).build());
465         bellini = updateUser(req).getEntity();
466         assertTrue(bellini.getRoles().contains(role.getKey()));
467 
468         // 5. now the instance of the type above can be created successfully
469         belliniClient.logout();
470         belliniClient.login(new BasicAuthenticationHandler("bellini", ADMIN_PWD));
471         belliniClient.getService(AnyObjectService.class).create(folder);
472     }
473 
474     @Test
475     public void asGroupOwner() {
476         // 0. prepare
477         UserTO owner = createUser(UserITCase.getUniqueSample("owner@syncope.org")).getEntity();
478         assertNotNull(owner);
479 
480         GroupCR groupCR = GroupITCase.getSample("forgroupownership");
481         groupCR.setUserOwner(owner.getKey());
482         GroupTO group = createGroup(groupCR).getEntity();
483         assertNotNull(group);
484         assertEquals(owner.getKey(), group.getUserOwner());
485 
486         UserCR memberCR = UserITCase.getUniqueSample("forgroupownership@syncope.org");
487         memberCR.getMemberships().add(new MembershipTO.Builder(group.getKey()).build());
488         memberCR.getMemberships().add(new MembershipTO.Builder("37d15e4c-cdc1-460b-a591-8505c8133806").build());
489         UserTO member = createUser(memberCR).getEntity();
490         assertEquals(2, member.getMemberships().size());
491         String memberKey = member.getKey();
492 
493         if (IS_EXT_SEARCH_ENABLED) {
494             try {
495                 Thread.sleep(2000);
496             } catch (InterruptedException ex) {
497                 // ignore
498             }
499         }
500 
501         PagedResult<UserTO> matching = USER_SERVICE.search(
502                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
503                         fiql(SyncopeClient.getUserSearchConditionBuilder().inGroups(group.getKey()).query()).
504                         page(1).size(1000).build());
505         int fullMatchSize = matching.getResult().size();
506         assertTrue(matching.getResult().stream().anyMatch(user -> memberKey.equals(user.getKey())));
507 
508         UserService groupOwnerService = CLIENT_FACTORY.create(owner.getUsername(), "password123").
509                 getService(UserService.class);
510 
511         // 1. search
512         matching = groupOwnerService.search(
513                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
514                         fiql(SyncopeClient.getUserSearchConditionBuilder().isNotNull("key").query()).
515                         page(1).size(1000).build());
516         assertEquals(fullMatchSize, matching.getResult().size());
517         assertTrue(matching.getResult().stream().anyMatch(user -> memberKey.equals(user.getKey())));
518 
519         // 2. update and read
520         UserUR memberUR = new UserUR();
521         memberUR.setKey(memberKey);
522         memberUR.setUsername(new StringReplacePatchItem.Builder().value("new" + getUUIDString()).build());
523 
524         Response response = groupOwnerService.update(memberUR);
525         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
526 
527         member = groupOwnerService.read(memberKey);
528         assertEquals(memberUR.getUsername().getValue(), member.getUsername());
529         assertEquals(2, member.getMemberships().size());
530 
531         // 3. update with membership removal -> fail
532         memberUR.setUsername(null);
533         memberUR.getMemberships().add(new MembershipUR.Builder(group.getKey()).
534                 operation(PatchOperation.DELETE).build());
535         try {
536             groupOwnerService.update(memberUR);
537             fail();
538         } catch (SyncopeClientException e) {
539             assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
540         }
541 
542         // 4. update non-member -> fail
543         UserTO nonmember = createUser(UserITCase.getUniqueSample("nonmember@syncope.org")).getEntity();
544         UserUR nonmemberUR = new UserUR();
545         nonmemberUR.setKey(nonmember.getKey());
546         nonmemberUR.setUsername(new StringReplacePatchItem.Builder().value("new" + getUUIDString()).build());
547         try {
548             groupOwnerService.update(nonmemberUR);
549             fail();
550         } catch (SyncopeClientException e) {
551             assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
552         }
553 
554         // 5. update user under /even
555         memberCR = UserITCase.getUniqueSample("forgroupownership2@syncope.org");
556         memberCR.setRealm("/even");
557         memberCR.getMemberships().add(new MembershipTO.Builder(group.getKey()).build());
558         member = createUser(memberCR).getEntity();
559 
560         memberUR = new UserUR();
561         memberUR.setKey(member.getKey());
562         memberUR.setUsername(new StringReplacePatchItem.Builder().value("new" + getUUIDString()).build());
563         response = groupOwnerService.update(memberUR);
564         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
565 
566         // 6 delete
567         groupOwnerService.delete(memberKey);
568         try {
569             USER_SERVICE.read(memberKey);
570             fail();
571         } catch (SyncopeClientException e) {
572             assertEquals(ClientExceptionType.NotFound, e.getType());
573         }
574     }
575 
576     @Test
577     public void issueSYNCOPE434() {
578         assumeTrue(IS_FLOWABLE_ENABLED);
579 
580         // 1. create user with group 'groupForWorkflowApproval' 
581         // (users with group groupForWorkflowApproval are defined in workflow as subject to approval)
582         UserCR userCR = UserITCase.getUniqueSample("createWithReject@syncope.apache.org");
583         userCR.getMemberships().add(new MembershipTO.Builder("0cbcabd2-4410-4b6b-8f05-a052b451d18f").build());
584 
585         UserTO userTO = createUser(userCR).getEntity();
586         assertNotNull(userTO);
587         assertEquals("createApproval", userTO.getStatus());
588 
589         // 2. try to authenticate: fail
590         try {
591             CLIENT_FACTORY.create(userTO.getUsername(), "password123").self();
592             fail("This should not happen");
593         } catch (AccessControlException e) {
594             assertNotNull(e);
595         }
596 
597         // 3. approve user
598         UserRequestForm form = USER_REQUEST_SERVICE.listForms(
599                 new UserRequestQuery.Builder().user(userTO.getKey()).build()).getResult().get(0);
600         form = USER_REQUEST_SERVICE.claimForm(form.getTaskId());
601         form.getProperty("approveCreate").get().setValue(Boolean.TRUE.toString());
602         userTO = USER_REQUEST_SERVICE.submitForm(form).readEntity(new GenericType<ProvisioningResult<UserTO>>() {
603         }).getEntity();
604         assertNotNull(userTO);
605         assertEquals("active", userTO.getStatus());
606 
607         // 4. try to authenticate again: success
608         Triple<Map<String, Set<String>>, List<String>, UserTO> self =
609                 CLIENT_FACTORY.create(userTO.getUsername(), "password123").self();
610         assertNotNull(self);
611         assertNotNull(self.getLeft());
612         assertEquals(List.of(), self.getMiddle());
613         assertNotNull(self.getRight());
614     }
615 
616     @Test
617     public void issueSYNCOPE164() throws Exception {
618         // 1. create user with db resource
619         UserCR userCR = UserITCase.getUniqueSample("syncope164@syncope.apache.org");
620         userCR.setRealm("/even/two");
621         userCR.setPassword("password123");
622         userCR.getResources().add(RESOURCE_NAME_TESTDB);
623         UserTO user = createUser(userCR).getEntity();
624         assertNotNull(user);
625 
626         // 2. unlink the resource from the created user
627         ResourceDR resourceDR = new ResourceDR.Builder().key(user.getKey()).
628                 action(ResourceDeassociationAction.UNLINK).resource(RESOURCE_NAME_TESTDB).build();
629         assertNotNull(parseBatchResponse(USER_SERVICE.deassociate(resourceDR)));
630 
631         // 3. change password on Syncope
632         UserUR userUR = new UserUR();
633         userUR.setKey(user.getKey());
634         userUR.setPassword(new PasswordPatch.Builder().value("password234").build());
635         user = updateUser(userUR).getEntity();
636         assertNotNull(user);
637 
638         // 4. check that the db resource has still the initial password value
639         JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
640         String value = queryForObject(jdbcTemplate, MAX_WAIT_SECONDS,
641                 "SELECT PASSWORD FROM test WHERE ID=?", String.class, user.getUsername());
642         assertEquals(Encryptor.getInstance().encode("password123", CipherAlgorithm.SHA1), value.toUpperCase());
643 
644         // 5. successfully authenticate with old (on db resource) and new (on internal storage) password values
645         Triple<Map<String, Set<String>>, List<String>, UserTO> self =
646                 CLIENT_FACTORY.create(user.getUsername(), "password123").self();
647         assertNotNull(self);
648         self = CLIENT_FACTORY.create(user.getUsername(), "password234").self();
649         assertNotNull(self);
650     }
651 
652     @Test
653     public void issueSYNCOPE706() {
654         String username = getUUIDString();
655         try {
656             USER_SERVICE.read(username);
657             fail("This should not happen");
658         } catch (SyncopeClientException e) {
659             assertEquals(ClientExceptionType.NotFound, e.getType());
660         }
661 
662         try {
663             CLIENT_FACTORY.create(username, "anypassword").self();
664             fail("This should not happen");
665         } catch (AccessControlException e) {
666             assertNotNull(e.getMessage());
667         }
668     }
669 }