1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.assertNotEquals;
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
27 import com.fasterxml.jackson.databind.node.ArrayNode;
28 import java.net.URI;
29 import java.net.http.HttpClient;
30 import java.net.http.HttpRequest;
31 import java.net.http.HttpRequest.BodyPublishers;
32 import java.net.http.HttpResponse;
33 import java.net.http.HttpResponse.BodyHandlers;
34 import javax.ws.rs.core.GenericType;
35 import javax.ws.rs.core.HttpHeaders;
36 import javax.ws.rs.core.MediaType;
37 import javax.ws.rs.core.Response;
38 import org.apache.syncope.client.lib.SyncopeClient;
39 import org.apache.syncope.common.lib.Attr;
40 import org.apache.syncope.common.lib.SyncopeClientException;
41 import org.apache.syncope.common.lib.SyncopeConstants;
42 import org.apache.syncope.common.lib.request.AttrPatch;
43 import org.apache.syncope.common.lib.request.GroupCR;
44 import org.apache.syncope.common.lib.request.GroupUR;
45 import org.apache.syncope.common.lib.request.StringPatchItem;
46 import org.apache.syncope.common.lib.request.UserCR;
47 import org.apache.syncope.common.lib.request.UserUR;
48 import org.apache.syncope.common.lib.to.DynRealmTO;
49 import org.apache.syncope.common.lib.to.GroupTO;
50 import org.apache.syncope.common.lib.to.PagedResult;
51 import org.apache.syncope.common.lib.to.ProvisioningResult;
52 import org.apache.syncope.common.lib.to.RoleTO;
53 import org.apache.syncope.common.lib.to.UserTO;
54 import org.apache.syncope.common.lib.types.AnyTypeKind;
55 import org.apache.syncope.common.lib.types.ClientExceptionType;
56 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
57 import org.apache.syncope.common.lib.types.PatchOperation;
58 import org.apache.syncope.common.rest.api.beans.AnyQuery;
59 import org.apache.syncope.common.rest.api.service.DynRealmService;
60 import org.apache.syncope.common.rest.api.service.GroupService;
61 import org.apache.syncope.common.rest.api.service.UserService;
62 import org.apache.syncope.fit.AbstractITCase;
63 import org.junit.jupiter.api.Test;
64
65 public class DynRealmITCase extends AbstractITCase {
66
67 private static ArrayNode fetchDynRealmsFromElasticsearch(final String userKey) throws Exception {
68 String body =
69 '{'
70 + " \"query\": {"
71 + " \"match\": {\"_id\": \"" + userKey + "\"}"
72 + " }"
73 + '}';
74
75 HttpClient client = HttpClient.newHttpClient();
76 HttpResponse<String> response = client.send(
77 HttpRequest.newBuilder(URI.create("http://localhost:9200/master_user/_search")).
78 header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).
79 method("GET", BodyPublishers.ofString(body)).
80 build(),
81 BodyHandlers.ofString());
82 assertEquals(Response.Status.OK.getStatusCode(), response.statusCode());
83
84 return (ArrayNode) JSON_MAPPER.readTree(response.body()).
85 get("hits").get("hits").get(0).get("_source").get("dynRealms");
86 }
87
88 @Test
89 public void misc() {
90 DynRealmTO dynRealm = null;
91 try {
92 dynRealm = new DynRealmTO();
93 dynRealm.setKey("/name" + getUUIDString());
94 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true");
95
96
97 try {
98 DYN_REALM_SERVICE.create(dynRealm);
99 fail("This should not happen");
100 } catch (SyncopeClientException e) {
101 assertEquals(ClientExceptionType.InvalidDynRealm, e.getType());
102 }
103 dynRealm.setKey("name" + getUUIDString());
104
105 Response response = DYN_REALM_SERVICE.create(dynRealm);
106 dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
107 assertNotNull(dynRealm);
108
109 PagedResult<UserTO> matching = USER_SERVICE.search(new AnyQuery.Builder().fiql("cool==true").build());
110 assertNotNull(matching);
111 assertNotEquals(0, matching.getSize());
112
113 UserTO user = matching.getResult().get(0);
114
115 assertTrue(user.getDynRealms().contains(dynRealm.getKey()));
116 } finally {
117 if (dynRealm != null) {
118 DYN_REALM_SERVICE.delete(dynRealm.getKey());
119 }
120 }
121 }
122
123 @Test
124 public void delegatedAdmin() {
125 DynRealmTO dynRealm = null;
126 RoleTO role = null;
127 try {
128
129 dynRealm = new DynRealmTO();
130 dynRealm.setKey("LDAPLovers" + getUUIDString());
131 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "$resources==resource-ldap");
132 dynRealm.getDynMembershipConds().put(AnyTypeKind.GROUP.name(), "$resources==resource-ldap");
133
134 Response response = DYN_REALM_SERVICE.create(dynRealm);
135 dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
136 assertNotNull(dynRealm);
137
138
139 role = new RoleTO();
140 role.setKey("Administer LDAP" + getUUIDString());
141 role.getEntitlements().add(IdRepoEntitlement.USER_SEARCH);
142 role.getEntitlements().add(IdRepoEntitlement.USER_READ);
143 role.getEntitlements().add(IdRepoEntitlement.USER_UPDATE);
144 role.getEntitlements().add(IdRepoEntitlement.GROUP_READ);
145 role.getEntitlements().add(IdRepoEntitlement.GROUP_UPDATE);
146 role.getDynRealms().add(dynRealm.getKey());
147
148 role = createRole(role);
149 assertNotNull(role);
150
151
152 UserCR dynRealmAdmin = UserITCase.getUniqueSample("dynRealmAdmin@apache.org");
153 dynRealmAdmin.setPassword("password123");
154 dynRealmAdmin.getRoles().add(role.getKey());
155 assertNotNull(createUser(dynRealmAdmin).getEntity());
156
157
158 UserCR userCR = UserITCase.getUniqueSample("dynRealmUser@apache.org");
159 userCR.setRealm("/even/two");
160 userCR.getResources().clear();
161 userCR.getResources().add(RESOURCE_NAME_LDAP);
162 UserTO user = createUser(userCR).getEntity();
163 assertNotNull(user);
164 final String userKey = user.getKey();
165
166 GroupCR groupCR = GroupITCase.getSample("dynRealmGroup");
167 groupCR.setRealm("/odd");
168 groupCR.getResources().clear();
169 groupCR.getResources().add(RESOURCE_NAME_LDAP);
170 GroupTO group = createGroup(groupCR).getEntity();
171 assertNotNull(group);
172 final String groupKey = group.getKey();
173
174 if (IS_EXT_SEARCH_ENABLED) {
175 try {
176 Thread.sleep(2000);
177 } catch (InterruptedException ex) {
178
179 }
180 }
181
182
183 PagedResult<UserTO> matchingUsers = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
184 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
185 assertTrue(matchingUsers.getResult().stream().anyMatch(object -> object.getKey().equals(userKey)));
186
187 PagedResult<GroupTO> matchingGroups = GROUP_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
188 SyncopeClient.getGroupSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
189 assertTrue(matchingGroups.getResult().stream().anyMatch(object -> object.getKey().equals(groupKey)));
190
191
192 SyncopeClient delegatedClient = CLIENT_FACTORY.create(dynRealmAdmin.getUsername(), "password123");
193 UserService delegatedUserService = delegatedClient.getService(UserService.class);
194 GroupService delegatedGroupService = delegatedClient.getService(GroupService.class);
195
196
197
198 assertNotNull(delegatedUserService.read(userKey));
199
200
201 assertNotNull(delegatedGroupService.read(groupKey));
202
203
204 matchingUsers = delegatedUserService.search(new AnyQuery.Builder().realm("/").build());
205 assertTrue(matchingUsers.getResult().stream().anyMatch(object -> object.getKey().equals(userKey)));
206
207
208 UserUR userUR = new UserUR();
209 userUR.setKey(userKey);
210 userUR.getResources().add(new StringPatchItem.Builder().
211 value(RESOURCE_NAME_LDAP).operation(PatchOperation.DELETE).build());
212
213 try {
214 delegatedUserService.update(userUR);
215 fail("This should not happen");
216 } catch (SyncopeClientException e) {
217 assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
218 }
219
220 userUR.getResources().clear();
221 userUR.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_NOPROPAGATION).build());
222 user = delegatedUserService.update(userUR).
223 readEntity(new GenericType<ProvisioningResult<UserTO>>() {
224 }).getEntity();
225 assertNotNull(user);
226 assertTrue(user.getResources().contains(RESOURCE_NAME_NOPROPAGATION));
227
228
229 GroupUR groupUR = new GroupUR();
230 groupUR.setKey(groupKey);
231 groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("icon", "modified")).build());
232 group = delegatedGroupService.update(groupUR).readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
233 }).getEntity();
234 assertNotNull(group);
235 assertEquals("modified", group.getPlainAttr("icon").get().getValues().get(0));
236 } finally {
237 if (role != null) {
238 ROLE_SERVICE.delete(role.getKey());
239 }
240 if (dynRealm != null) {
241 DYN_REALM_SERVICE.delete(dynRealm.getKey());
242 }
243 }
244 }
245
246 @Test
247 public void issueSYNCOPE1480() throws Exception {
248 String ctype = getUUIDString();
249
250 DynRealmTO dynRealm = null;
251 try {
252
253 dynRealm = new DynRealmTO();
254 dynRealm.setKey("name" + getUUIDString());
255 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "ctype==" + ctype);
256
257 Response response = DYN_REALM_SERVICE.create(dynRealm);
258 dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
259 assertNotNull(dynRealm);
260
261
262 PagedResult<UserTO> matching = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
263 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
264 assertEquals(0, matching.getSize());
265
266
267 UserCR userCR = UserITCase.getUniqueSample("syncope1480@syncope.apache.org");
268 userCR.getPlainAttr("ctype").get().getValues().set(0, ctype);
269 UserTO user = createUser(userCR).getEntity();
270 assertNotNull(user.getKey());
271
272
273 if (IS_EXT_SEARCH_ENABLED) {
274 try {
275 Thread.sleep(2000);
276 } catch (InterruptedException ex) {
277
278 }
279
280 ArrayNode dynRealms = fetchDynRealmsFromElasticsearch(user.getKey());
281 assertEquals(1, dynRealms.size());
282 assertEquals(dynRealm.getKey(), dynRealms.get(0).asText());
283 }
284
285
286 matching = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).fiql(
287 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
288 assertEquals(1, matching.getSize());
289
290
291 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "ctype==ANY");
292 DYN_REALM_SERVICE.update(dynRealm);
293
294
295 if (IS_EXT_SEARCH_ENABLED) {
296 try {
297 Thread.sleep(2000);
298 } catch (InterruptedException ex) {
299
300 }
301
302 ArrayNode dynRealms = fetchDynRealmsFromElasticsearch(user.getKey());
303 assertTrue(dynRealms.isEmpty());
304 }
305
306
307 matching = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).fiql(
308 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
309 assertEquals(0, matching.getSize());
310 } finally {
311 if (dynRealm != null) {
312 DYN_REALM_SERVICE.delete(dynRealm.getKey());
313 }
314 }
315 }
316
317 @Test
318 public void issueSYNCOPE1806() {
319 DynRealmTO realm1 = null;
320 DynRealmTO realm2 = null;
321 try {
322
323 realm1 = new DynRealmTO();
324 realm1.setKey("realm1");
325 realm1.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true");
326 realm1 = getObject(DYN_REALM_SERVICE.create(realm1).getLocation(), DynRealmService.class, DynRealmTO.class);
327 assertNotNull(realm1);
328
329 realm2 = new DynRealmTO();
330 realm2.setKey("realm2");
331 realm2.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true");
332 realm2 = getObject(DYN_REALM_SERVICE.create(realm2).getLocation(), DynRealmService.class, DynRealmTO.class);
333 assertNotNull(realm2);
334
335
336 if (IS_EXT_SEARCH_ENABLED) {
337 try {
338 Thread.sleep(2000);
339 } catch (InterruptedException ex) {
340
341 }
342 }
343
344 PagedResult<UserTO> matching1 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
345 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm1.getKey()).query()).build());
346 PagedResult<UserTO> matching2 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
347 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm2.getKey()).query()).build());
348
349 assertEquals(matching1, matching2);
350 assertEquals(1, matching1.getResult().size());
351 assertTrue(matching1.getResult().stream().
352 anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
353
354
355 UserUR userUR = new UserUR();
356 userUR.setKey("823074dc-d280-436d-a7dd-07399fae48ec");
357 userUR.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder("cool").value("true").build()).build());
358 updateUser(userUR);
359
360
361 if (IS_EXT_SEARCH_ENABLED) {
362 try {
363 Thread.sleep(2000);
364 } catch (InterruptedException ex) {
365
366 }
367 }
368
369 matching1 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
370 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm1.getKey()).query()).build());
371 matching2 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
372 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm2.getKey()).query()).build());
373 assertEquals(matching1, matching2);
374 assertEquals(2, matching1.getResult().size());
375 assertTrue(matching1.getResult().stream().
376 anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
377 assertTrue(matching1.getResult().stream().
378 anyMatch(u -> "823074dc-d280-436d-a7dd-07399fae48ec".equals(u.getKey())));
379 } finally {
380 if (realm1 != null) {
381 DYN_REALM_SERVICE.delete(realm1.getKey());
382 }
383 if (realm2 != null) {
384 DYN_REALM_SERVICE.delete(realm2.getKey());
385 }
386 UserUR userUR = new UserUR();
387 userUR.setKey("823074dc-d280-436d-a7dd-07399fae48ec");
388 userUR.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder("cool").build()).
389 operation(PatchOperation.DELETE).build());
390 updateUser(userUR);
391 }
392 }
393 }