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.nio.charset.StandardCharsets;
29 import javax.ws.rs.core.GenericType;
30 import javax.ws.rs.core.MediaType;
31 import javax.ws.rs.core.Response;
32 import org.apache.commons.io.IOUtils;
33 import org.apache.syncope.client.lib.SyncopeClient;
34 import org.apache.syncope.common.lib.SyncopeClientException;
35 import org.apache.syncope.common.lib.SyncopeConstants;
36 import org.apache.syncope.common.lib.request.AttrPatch;
37 import org.apache.syncope.common.lib.request.GroupCR;
38 import org.apache.syncope.common.lib.request.GroupUR;
39 import org.apache.syncope.common.lib.request.StringPatchItem;
40 import org.apache.syncope.common.lib.request.UserCR;
41 import org.apache.syncope.common.lib.request.UserUR;
42 import org.apache.syncope.common.lib.to.DynRealmTO;
43 import org.apache.syncope.common.lib.to.GroupTO;
44 import org.apache.syncope.common.lib.to.PagedResult;
45 import org.apache.syncope.common.lib.to.ProvisioningResult;
46 import org.apache.syncope.common.lib.to.RoleTO;
47 import org.apache.syncope.common.lib.to.UserTO;
48 import org.apache.syncope.common.lib.types.AnyTypeKind;
49 import org.apache.syncope.common.lib.types.ClientExceptionType;
50 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
51 import org.apache.syncope.common.lib.types.PatchOperation;
52 import org.apache.syncope.common.rest.api.beans.AnyQuery;
53 import org.apache.syncope.common.rest.api.service.DynRealmService;
54 import org.apache.syncope.common.rest.api.service.GroupService;
55 import org.apache.syncope.common.rest.api.service.UserService;
56 import org.apache.syncope.fit.AbstractITCase;
57 import org.eclipse.jetty.client.HttpClient;
58 import org.eclipse.jetty.client.api.ContentResponse;
59 import org.eclipse.jetty.client.util.InputStreamContentProvider;
60 import org.eclipse.jetty.http.HttpHeader;
61 import org.eclipse.jetty.http.HttpMethod;
62 import org.eclipse.jetty.http.HttpStatus;
63 import org.junit.jupiter.api.Test;
64
65 public class DynRealmITCase extends AbstractITCase {
66
67 @Test
68 public void misc() {
69 DynRealmTO dynRealm = null;
70 try {
71 dynRealm = new DynRealmTO();
72 dynRealm.setKey("/name" + getUUIDString());
73 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true");
74
75
76 try {
77 DYN_REALM_SERVICE.create(dynRealm);
78 fail("This should not happen");
79 } catch (SyncopeClientException e) {
80 assertEquals(ClientExceptionType.InvalidDynRealm, e.getType());
81 }
82 dynRealm.setKey("name" + getUUIDString());
83
84 Response response = DYN_REALM_SERVICE.create(dynRealm);
85 dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
86 assertNotNull(dynRealm);
87
88 PagedResult<UserTO> matching = USER_SERVICE.search(new AnyQuery.Builder().fiql("cool==true").build());
89 assertNotNull(matching);
90 assertNotEquals(0, matching.getSize());
91
92 UserTO user = matching.getResult().get(0);
93
94 assertTrue(user.getDynRealms().contains(dynRealm.getKey()));
95 } finally {
96 if (dynRealm != null) {
97 DYN_REALM_SERVICE.delete(dynRealm.getKey());
98 }
99 }
100 }
101
102 @Test
103 public void delegatedAdmin() {
104 DynRealmTO dynRealm = null;
105 RoleTO role = null;
106 try {
107
108 dynRealm = new DynRealmTO();
109 dynRealm.setKey("LDAPLovers" + getUUIDString());
110 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "$resources==resource-ldap");
111 dynRealm.getDynMembershipConds().put(AnyTypeKind.GROUP.name(), "$resources==resource-ldap");
112
113 Response response = DYN_REALM_SERVICE.create(dynRealm);
114 dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
115 assertNotNull(dynRealm);
116
117
118 role = new RoleTO();
119 role.setKey("Administer LDAP" + getUUIDString());
120 role.getEntitlements().add(IdRepoEntitlement.USER_SEARCH);
121 role.getEntitlements().add(IdRepoEntitlement.USER_READ);
122 role.getEntitlements().add(IdRepoEntitlement.USER_UPDATE);
123 role.getEntitlements().add(IdRepoEntitlement.GROUP_READ);
124 role.getEntitlements().add(IdRepoEntitlement.GROUP_UPDATE);
125 role.getDynRealms().add(dynRealm.getKey());
126
127 role = createRole(role);
128 assertNotNull(role);
129
130
131 UserCR dynRealmAdmin = UserITCase.getUniqueSample("dynRealmAdmin@apache.org");
132 dynRealmAdmin.setPassword("password123");
133 dynRealmAdmin.getRoles().add(role.getKey());
134 assertNotNull(createUser(dynRealmAdmin).getEntity());
135
136
137 UserCR userCR = UserITCase.getUniqueSample("dynRealmUser@apache.org");
138 userCR.setRealm("/even/two");
139 userCR.getResources().clear();
140 userCR.getResources().add(RESOURCE_NAME_LDAP);
141 UserTO user = createUser(userCR).getEntity();
142 assertNotNull(user);
143 final String userKey = user.getKey();
144
145 GroupCR groupCR = GroupITCase.getSample("dynRealmGroup");
146 groupCR.setRealm("/odd");
147 groupCR.getResources().clear();
148 groupCR.getResources().add(RESOURCE_NAME_LDAP);
149 GroupTO group = createGroup(groupCR).getEntity();
150 assertNotNull(group);
151 final String groupKey = group.getKey();
152
153 if (IS_EXT_SEARCH_ENABLED) {
154 try {
155 Thread.sleep(2000);
156 } catch (InterruptedException ex) {
157
158 }
159 }
160
161
162 PagedResult<UserTO> matchingUsers = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
163 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
164 assertTrue(matchingUsers.getResult().stream().anyMatch(object -> object.getKey().equals(userKey)));
165
166 PagedResult<GroupTO> matchingGroups = GROUP_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
167 SyncopeClient.getGroupSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
168 assertTrue(matchingGroups.getResult().stream().anyMatch(object -> object.getKey().equals(groupKey)));
169
170
171 SyncopeClient delegatedClient = CLIENT_FACTORY.create(dynRealmAdmin.getUsername(), "password123");
172 UserService delegatedUserService = delegatedClient.getService(UserService.class);
173 GroupService delegatedGroupService = delegatedClient.getService(GroupService.class);
174
175
176
177 assertNotNull(delegatedUserService.read(userKey));
178
179
180 assertNotNull(delegatedGroupService.read(groupKey));
181
182
183 matchingUsers = delegatedUserService.search(new AnyQuery.Builder().realm("/").build());
184 assertTrue(matchingUsers.getResult().stream().anyMatch(object -> object.getKey().equals(userKey)));
185
186
187 UserUR userUR = new UserUR();
188 userUR.setKey(userKey);
189 userUR.getResources().add(new StringPatchItem.Builder().
190 value(RESOURCE_NAME_LDAP).operation(PatchOperation.DELETE).build());
191
192 try {
193 delegatedUserService.update(userUR);
194 fail("This should not happen");
195 } catch (SyncopeClientException e) {
196 assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
197 }
198
199 userUR.getResources().clear();
200 userUR.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_NOPROPAGATION).build());
201 user = delegatedUserService.update(userUR).
202 readEntity(new GenericType<ProvisioningResult<UserTO>>() {
203 }).getEntity();
204 assertNotNull(user);
205 assertTrue(user.getResources().contains(RESOURCE_NAME_NOPROPAGATION));
206
207
208 GroupUR groupUR = new GroupUR();
209 groupUR.setKey(groupKey);
210 groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("icon", "modified")).build());
211 group = delegatedGroupService.update(groupUR).readEntity(new GenericType<ProvisioningResult<GroupTO>>() {
212 }).getEntity();
213 assertNotNull(group);
214 assertEquals("modified", group.getPlainAttr("icon").get().getValues().get(0));
215 } finally {
216 if (role != null) {
217 ROLE_SERVICE.delete(role.getKey());
218 }
219 if (dynRealm != null) {
220 DYN_REALM_SERVICE.delete(dynRealm.getKey());
221 }
222 }
223 }
224
225 private static ArrayNode fetchDynRealmsFromElasticsearch(final String userKey) throws Exception {
226 String body =
227 '{'
228 + " \"query\": {"
229 + " \"match\": {\"_id\": \"" + userKey + "\"}"
230 + " }"
231 + '}';
232
233 HttpClient httpClient = new HttpClient();
234 httpClient.start();
235 ContentResponse response = httpClient.newRequest("http://localhost:9200/master_user/_search").
236 method(HttpMethod.GET).
237 header(HttpHeader.CONTENT_TYPE, MediaType.APPLICATION_JSON).
238 content(new InputStreamContentProvider(IOUtils.toInputStream(body, StandardCharsets.UTF_8))).
239 send();
240 assertEquals(HttpStatus.OK_200, response.getStatus());
241
242 return (ArrayNode) JSON_MAPPER.readTree(response.getContent()).
243 get("hits").get("hits").get(0).get("_source").get("dynRealms");
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 DYN_REALM_SERVICE.create(dynRealm);
257
258 Response response = DYN_REALM_SERVICE.create(dynRealm);
259 dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
260 assertNotNull(dynRealm);
261
262
263 PagedResult<UserTO> matching = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
264 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
265 assertEquals(0, matching.getSize());
266
267
268 UserCR userCR = UserITCase.getUniqueSample("syncope1480@syncope.apache.org");
269 userCR.getPlainAttr("ctype").get().getValues().set(0, ctype);
270 UserTO user = createUser(userCR).getEntity();
271 assertNotNull(user.getKey());
272
273
274 if (IS_EXT_SEARCH_ENABLED) {
275 try {
276 Thread.sleep(2000);
277 } catch (InterruptedException ex) {
278
279 }
280
281 ArrayNode dynRealms = fetchDynRealmsFromElasticsearch(user.getKey());
282 assertEquals(1, dynRealms.size());
283 assertEquals(dynRealm.getKey(), dynRealms.get(0).asText());
284 }
285
286
287 matching = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).fiql(
288 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
289 assertEquals(1, matching.getSize());
290
291
292 dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "ctype==ANY");
293 DYN_REALM_SERVICE.update(dynRealm);
294
295
296 if (IS_EXT_SEARCH_ENABLED) {
297 try {
298 Thread.sleep(2000);
299 } catch (InterruptedException ex) {
300
301 }
302
303 ArrayNode dynRealms = fetchDynRealmsFromElasticsearch(user.getKey());
304 assertTrue(dynRealms.isEmpty());
305 }
306
307
308 matching = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).fiql(
309 SyncopeClient.getUserSearchConditionBuilder().inDynRealms(dynRealm.getKey()).query()).build());
310 assertEquals(0, matching.getSize());
311 } finally {
312 if (dynRealm != null) {
313 DYN_REALM_SERVICE.delete(dynRealm.getKey());
314 }
315 }
316 }
317 }