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.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.Assumptions.assumeTrue;
28
29 import com.fasterxml.jackson.core.JsonProcessingException;
30 import com.fasterxml.jackson.databind.SerializationFeature;
31 import com.fasterxml.jackson.databind.json.JsonMapper;
32 import com.fasterxml.jackson.databind.node.ArrayNode;
33 import com.fasterxml.jackson.databind.node.ObjectNode;
34 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
35 import java.io.IOException;
36 import java.time.OffsetDateTime;
37 import java.time.format.DateTimeFormatter;
38 import java.time.temporal.ChronoUnit;
39 import java.util.List;
40 import java.util.UUID;
41 import javax.ws.rs.HttpMethod;
42 import javax.ws.rs.core.GenericType;
43 import javax.ws.rs.core.HttpHeaders;
44 import javax.ws.rs.core.Response;
45 import org.apache.commons.lang3.StringUtils;
46 import org.apache.cxf.jaxrs.client.WebClient;
47 import org.apache.syncope.common.lib.scim.SCIMComplexConf;
48 import org.apache.syncope.common.lib.scim.SCIMConf;
49 import org.apache.syncope.common.lib.scim.SCIMGroupConf;
50 import org.apache.syncope.common.lib.scim.SCIMUserConf;
51 import org.apache.syncope.common.lib.scim.SCIMUserNameConf;
52 import org.apache.syncope.common.lib.scim.types.EmailCanonicalType;
53 import org.apache.syncope.common.lib.to.ProvisioningResult;
54 import org.apache.syncope.common.lib.to.UserTO;
55 import org.apache.syncope.ext.scimv2.api.SCIMConstants;
56 import org.apache.syncope.ext.scimv2.api.data.Group;
57 import org.apache.syncope.ext.scimv2.api.data.ListResponse;
58 import org.apache.syncope.ext.scimv2.api.data.Member;
59 import org.apache.syncope.ext.scimv2.api.data.ResourceType;
60 import org.apache.syncope.ext.scimv2.api.data.SCIMComplexValue;
61 import org.apache.syncope.ext.scimv2.api.data.SCIMError;
62 import org.apache.syncope.ext.scimv2.api.data.SCIMGroup;
63 import org.apache.syncope.ext.scimv2.api.data.SCIMSearchRequest;
64 import org.apache.syncope.ext.scimv2.api.data.SCIMUser;
65 import org.apache.syncope.ext.scimv2.api.data.SCIMUserName;
66 import org.apache.syncope.ext.scimv2.api.data.ServiceProviderConfig;
67 import org.apache.syncope.ext.scimv2.api.data.Value;
68 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
69 import org.apache.syncope.ext.scimv2.api.type.Resource;
70 import org.apache.syncope.fit.AbstractITCase;
71 import org.junit.jupiter.api.BeforeAll;
72 import org.junit.jupiter.api.Test;
73
74 public class SCIMITCase extends AbstractITCase {
75
76 public static final String SCIM_ADDRESS = "http://localhost:9080/syncope/rest/scim/v2";
77
78 private static final SCIMConf CONF;
79
80 private static Boolean ENABLED;
81
82 static {
83 CONF = new SCIMConf();
84
85 CONF.setGroupConf(new SCIMGroupConf());
86
87 CONF.getGroupConf().setExternalId("originalName");
88
89 CONF.setUserConf(new SCIMUserConf());
90
91 CONF.getUserConf().setNickName("ctype");
92 CONF.getUserConf().setDisplayName("cn");
93
94 CONF.getUserConf().setName(new SCIMUserNameConf());
95 CONF.getUserConf().getName().setGivenName("firstname");
96 CONF.getUserConf().getName().setFamilyName("surname");
97 CONF.getUserConf().getName().setFormatted("fullname");
98
99 SCIMComplexConf<EmailCanonicalType> email = new SCIMComplexConf<>();
100 email.setValue("userId");
101 email.setType(EmailCanonicalType.work);
102 CONF.getUserConf().getEmails().add(email);
103 email = new SCIMComplexConf<>();
104 email.setValue("email");
105 email.setType(EmailCanonicalType.home);
106 CONF.getUserConf().getEmails().add(email);
107 }
108
109 private static SCIMUser getSampleUser(final String username) {
110 SCIMUser user = new SCIMUser(null, List.of(Resource.User.schema()), null, username, true);
111 user.setPassword("password123");
112
113 SCIMUserName name = new SCIMUserName();
114 name.setGivenName(username);
115 name.setFamilyName("surname");
116 name.setFormatted(username);
117 user.setName(name);
118
119 SCIMComplexValue userId = new SCIMComplexValue();
120 userId.setType(EmailCanonicalType.work.name());
121 userId.setValue(username + "@syncope.apache.org");
122 user.getEmails().add(userId);
123
124 SCIMComplexValue email = new SCIMComplexValue();
125 email.setType(EmailCanonicalType.home.name());
126 email.setValue(username + "@syncope.apache.org");
127 user.getEmails().add(email);
128
129 return user;
130 }
131
132 @BeforeAll
133 public static void isSCIMAvailable() {
134 if (ENABLED == null) {
135 try {
136 Response response = webClient().path("ServiceProviderConfig").get();
137 ENABLED = response.getStatus() == 200;
138 } catch (Exception e) {
139
140 ENABLED = false;
141 }
142 }
143
144 assumeTrue(ENABLED);
145 }
146
147 private static WebClient webClient() {
148 return WebClient.create(
149 SCIM_ADDRESS,
150 List.of(new JacksonJsonProvider(JsonMapper.builder().
151 findAndAddModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build()))).
152 accept(SCIMConstants.APPLICATION_SCIM_JSON_TYPE).
153 type(SCIMConstants.APPLICATION_SCIM_JSON_TYPE).
154 header(HttpHeaders.AUTHORIZATION, "Bearer " + ADMIN_CLIENT.getJWT());
155 }
156
157 @Test
158 public void serviceProviderConfig() {
159 Response response = webClient().path("ServiceProviderConfig").get();
160 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
161 assertEquals(
162 SCIMConstants.APPLICATION_SCIM_JSON,
163 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
164
165 ServiceProviderConfig serviceProviderConfig = response.readEntity(ServiceProviderConfig.class);
166 assertNotNull(serviceProviderConfig);
167 assertTrue(serviceProviderConfig.getPatch().isSupported());
168 assertFalse(serviceProviderConfig.getBulk().isSupported());
169 assertTrue(serviceProviderConfig.getChangePassword().isSupported());
170 assertTrue(serviceProviderConfig.getEtag().isSupported());
171 assertTrue(serviceProviderConfig.getSort().isSupported());
172 }
173
174 @Test
175 public void resourceTypes() {
176 Response response = webClient().path("ResourceTypes").get();
177 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
178 assertEquals(
179 SCIMConstants.APPLICATION_SCIM_JSON,
180 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
181
182 List<ResourceType> resourceTypes = response.readEntity(new GenericType<>() {
183 });
184 assertNotNull(resourceTypes);
185 assertEquals(2, resourceTypes.size());
186
187 response = webClient().path("ResourceTypes").path("User").get();
188 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
189
190 ResourceType user = response.readEntity(ResourceType.class);
191 assertNotNull(user);
192 assertEquals(Resource.User.schema(), user.getSchema());
193 assertFalse(user.getSchemaExtensions().isEmpty());
194 }
195
196 @Test
197 public void schemas() {
198 Response response = webClient().path("Schemas").get();
199 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
200 assertEquals(
201 SCIMConstants.APPLICATION_SCIM_JSON,
202 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
203
204 ArrayNode schemas = response.readEntity(ArrayNode.class);
205 assertNotNull(schemas);
206 assertEquals(3, schemas.size());
207
208 response = webClient().path("Schemas").path("none").get();
209 assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
210
211 response = webClient().path("Schemas").path(Resource.EnterpriseUser.schema()).get();
212 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
213
214 ObjectNode enterpriseUser = response.readEntity(ObjectNode.class);
215 assertNotNull(enterpriseUser);
216 assertEquals(Resource.EnterpriseUser.schema(), enterpriseUser.get("id").textValue());
217 }
218
219 @Test
220 public void read() throws IOException {
221 Response response = webClient().path("Users").path("missing").get();
222 assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
223
224 SCIMError error = response.readEntity(SCIMError.class);
225 assertEquals(Response.Status.NOT_FOUND.getStatusCode(), error.getStatus());
226
227 response = webClient().path("Users").path("1417acbe-cbf6-4277-9372-e75e04f97000").get();
228 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
229 assertEquals(
230 SCIMConstants.APPLICATION_SCIM_JSON,
231 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
232
233 SCIMUser user = response.readEntity(SCIMUser.class);
234 assertNotNull(user);
235 assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", user.getId());
236 assertEquals("rossini", user.getUserName());
237 assertFalse(user.getGroups().isEmpty());
238 assertFalse(user.getRoles().isEmpty());
239
240 response = webClient().path("Users").path("1417acbe-cbf6-4277-9372-e75e04f97000").
241 query("attributes", "groups").get();
242 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
243 assertEquals(
244 SCIMConstants.APPLICATION_SCIM_JSON,
245 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
246
247 user = response.readEntity(SCIMUser.class);
248 assertNotNull(user);
249 assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", user.getId());
250 assertNull(user.getUserName());
251 assertFalse(user.getGroups().isEmpty());
252 assertTrue(user.getRoles().isEmpty());
253 }
254
255 @Test
256 public void conf() {
257 SCIMConf conf = SCIM_CONF_SERVICE.get();
258 assertNotNull(conf);
259
260 SCIM_CONF_SERVICE.set(CONF);
261
262 Response response = webClient().path("Users").path("1417acbe-cbf6-4277-9372-e75e04f97000").get();
263 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
264 assertEquals(
265 SCIMConstants.APPLICATION_SCIM_JSON,
266 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
267
268 SCIMUser user = response.readEntity(SCIMUser.class);
269 assertNotNull(user);
270 assertEquals("Rossini, Gioacchino", user.getDisplayName());
271 }
272
273 @Test
274 public void list() throws IOException {
275 Response response = webClient().path("Groups").query("count", 1100000).get();
276 assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
277 SCIMError error = response.readEntity(SCIMError.class);
278 assertEquals(ErrorType.tooMany, error.getScimType());
279
280 response = webClient().path("Groups").
281 query("sortBy", "displayName").
282 query("count", 11).
283 get();
284 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
285 assertEquals(
286 SCIMConstants.APPLICATION_SCIM_JSON,
287 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
288
289 ListResponse<SCIMGroup> result = response.readEntity(new GenericType<>() {
290 });
291 assertNotNull(result);
292 assertTrue(result.getTotalResults() > 0);
293 assertEquals(11, result.getItemsPerPage());
294
295 assertFalse(result.getResources().isEmpty());
296 result.getResources().forEach(group -> {
297 assertNotNull(group.getId());
298 assertNotNull(group.getDisplayName());
299 });
300 }
301
302 @Test
303 public void search() {
304
305 Response response = webClient().path("Groups").query("filter", "invalid").get();
306 assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
307
308 SCIMError error = response.readEntity(SCIMError.class);
309 assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), error.getStatus());
310 assertEquals(ErrorType.invalidFilter, error.getScimType());
311
312
313 response = webClient().path("Groups").query("filter", "displayName eq \"additional\"").get();
314 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
315 assertEquals(
316 SCIMConstants.APPLICATION_SCIM_JSON,
317 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
318
319 ListResponse<SCIMGroup> groups = response.readEntity(new GenericType<>() {
320 });
321 assertNotNull(groups);
322 assertEquals(1, groups.getTotalResults());
323
324 SCIMGroup additional = groups.getResources().get(0);
325 assertEquals("additional", additional.getDisplayName());
326
327
328 SCIMSearchRequest request = new SCIMSearchRequest("displayName eq \"additional\"", null, null, null, null);
329 response = webClient().path("Groups").path("/.search").post(request);
330 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
331 assertEquals(
332 SCIMConstants.APPLICATION_SCIM_JSON,
333 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
334
335 groups = response.readEntity(new GenericType<>() {
336 });
337 assertNotNull(groups);
338 assertEquals(1, groups.getTotalResults());
339
340 additional = groups.getResources().get(0);
341 assertEquals("additional", additional.getDisplayName());
342
343
344 UserTO newUser = USER_SERVICE.create(UserITCase.getUniqueSample("scimsearch@syncope.apache.org")).
345 readEntity(new GenericType<ProvisioningResult<UserTO>>() {
346 }).getEntity();
347
348 OffsetDateTime value = newUser.getCreationDate().minusSeconds(1).truncatedTo(ChronoUnit.SECONDS);
349 response = webClient().path("Users").query("filter", "meta.created gt \""
350 + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value) + '"').get();
351 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
352 assertEquals(
353 SCIMConstants.APPLICATION_SCIM_JSON,
354 StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
355
356 ListResponse<SCIMUser> users = response.readEntity(new GenericType<>() {
357 });
358 assertNotNull(users);
359 assertEquals(1, users.getTotalResults());
360
361 SCIMUser newSCIMUser = users.getResources().get(0);
362 assertEquals(newUser.getUsername(), newSCIMUser.getUserName());
363 }
364
365 @Test
366 public void createUser() throws JsonProcessingException {
367 SCIM_CONF_SERVICE.set(CONF);
368
369 SCIMUser user = getSampleUser(UUID.randomUUID().toString());
370 user.getRoles().add(new Value("User reviewer"));
371 user.getGroups().add(new Group("37d15e4c-cdc1-460b-a591-8505c8133806", null, null, null));
372
373 Response response = webClient().path("Users").post(user);
374 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
375
376 user = response.readEntity(SCIMUser.class);
377 assertNotNull(user.getId());
378 assertTrue(response.getLocation().toASCIIString().endsWith(user.getId()));
379
380 UserTO userTO = USER_SERVICE.read(user.getId());
381 assertEquals(user.getUserName(), userTO.getUsername());
382 assertTrue(user.isActive());
383 assertEquals(user.getDisplayName(), userTO.getDerAttr("cn").get().getValues().get(0));
384 assertEquals(user.getName().getGivenName(), userTO.getPlainAttr("firstname").get().getValues().get(0));
385 assertEquals(user.getName().getFamilyName(), userTO.getPlainAttr("surname").get().getValues().get(0));
386 assertEquals(user.getName().getFormatted(), userTO.getPlainAttr("fullname").get().getValues().get(0));
387 assertEquals(user.getEmails().get(0).getValue(), userTO.getPlainAttr("userId").get().getValues().get(0));
388 assertEquals(user.getEmails().get(1).getValue(), userTO.getPlainAttr("email").get().getValues().get(0));
389 assertEquals(user.getRoles().get(0).getValue(), userTO.getRoles().get(0));
390 assertEquals(user.getGroups().get(0).getValue(), userTO.getMemberships().get(0).getGroupKey());
391 }
392
393 @Test
394 public void updateUser() {
395 SCIM_CONF_SERVICE.set(CONF);
396
397 SCIMUser user = getSampleUser(UUID.randomUUID().toString());
398
399 Response response = webClient().path("Users").post(user);
400 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
401
402 user = response.readEntity(SCIMUser.class);
403 assertNotNull(user.getId());
404 assertNull(user.getNickName());
405 assertTrue(user.isActive());
406
407
408 String body =
409 "{"
410 + " \"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
411 + " \"Operations\": ["
412 + " {"
413 + " \"op\": \"add\","
414 + " \"value\": {"
415 + " \"nickName\": \"" + user.getUserName() + "\","
416 + " \"active\": false"
417 + " }"
418 + " }"
419 + " ]"
420 + "}";
421 response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
422 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
423
424 user = response.readEntity(SCIMUser.class);
425 assertEquals(user.getUserName(), user.getNickName());
426 assertFalse(user.isActive());
427
428
429 body =
430 "{"
431 + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
432 + "\"Operations\":[{"
433 + "\"op\":\"Replace\","
434 + "\"path\":\"active\","
435 + "\"value\":true"
436 + "}]"
437 + "}";
438 response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
439 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
440
441 user = response.readEntity(SCIMUser.class);
442 assertTrue(user.isActive());
443
444
445 assertNotEquals("newSurname", user.getName().getFamilyName());
446 body =
447 "{"
448 + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
449 + "\"Operations\":["
450 + "{"
451 + "\"op\":\"Replace\","
452 + "\"path\":\"name.familyName\","
453 + "\"value\":\"newSurname\""
454 + "},"
455 + "{"
456 + "\"op\":\"remove\","
457 + "\"path\":\"nickName\""
458 + "}"
459 + "]"
460 + "}";
461 response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
462 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
463
464 user = response.readEntity(SCIMUser.class);
465 assertEquals("newSurname", user.getName().getFamilyName());
466 assertNull(user.getNickName());
467
468
469 String newMail = UUID.randomUUID().toString() + "@syncope.apache.org";
470 assertNotEquals(
471 newMail,
472 user.getEmails().stream().filter(v -> "work".equals(v.getType())).findFirst().get().getValue());
473 body =
474 "{"
475 + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
476 + " \"Operations\": [{"
477 + " \"op\":\"replace\","
478 + " \"path\":\"emails[type eq \\\"work\\\"]\","
479 + " \"value\":"
480 + " {"
481 + " \"type\": \"work\","
482 + " \"value\": \"" + newMail + "\","
483 + " \"primary\": true"
484 + " }"
485 + " }]"
486 + " }";
487 response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
488 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
489
490 user = response.readEntity(SCIMUser.class);
491 assertEquals(
492 newMail,
493 user.getEmails().stream().filter(v -> "work".equals(v.getType())).findFirst().get().getValue());
494
495
496 newMail = "verycomplex" + UUID.randomUUID().toString() + "@syncope.apache.org";
497 body =
498 "{"
499 + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
500 + " \"Operations\": [{"
501 + " \"op\":\"replace\","
502 + " \"path\":\"emails[type eq \\\"work\\\"].value\","
503 + " \"value\":\"" + newMail + "\""
504 + " }]"
505 + " }";
506 response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
507 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
508
509 user = response.readEntity(SCIMUser.class);
510 assertEquals(
511 newMail,
512 user.getEmails().stream().filter(v -> "work".equals(v.getType())).findFirst().get().getValue());
513
514
515 body =
516 "{"
517 + " \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
518 + " \"Operations\": [{"
519 + " \"op\":\"remove\","
520 + " \"path\":\"emails[type eq \\\"home\\\"]\""
521 + " }]"
522 + " }";
523 response = webClient().path("Users").path(user.getId()).invoke(HttpMethod.PATCH, body);
524 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
525
526 user = response.readEntity(SCIMUser.class);
527 assertTrue(user.getEmails().stream().noneMatch(v -> "home".equals(v.getType())));
528 }
529
530 @Test
531 public void replaceUser() {
532 SCIM_CONF_SERVICE.set(CONF);
533
534 SCIMUser user = getSampleUser(UUID.randomUUID().toString());
535
536 Response response = webClient().path("Users").post(user);
537 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
538
539 user = response.readEntity(SCIMUser.class);
540 assertNotNull(user.getId());
541
542 user.getName().setFormatted("new" + user.getUserName());
543
544 response = webClient().path("Users").path(user.getId()).put(user);
545 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
546
547 user = response.readEntity(SCIMUser.class);
548 assertTrue(user.getName().getFormatted().startsWith("new"));
549 }
550
551 @Test
552 public void deleteUser() {
553 SCIM_CONF_SERVICE.set(CONF);
554
555 SCIMUser user = getSampleUser(UUID.randomUUID().toString());
556
557 Response response = webClient().path("Users").post(user);
558 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
559
560 user = response.readEntity(SCIMUser.class);
561 assertNotNull(user.getId());
562
563 response = webClient().path("Users").path(user.getId()).get();
564 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
565
566 response = webClient().path("Users").path(user.getId()).delete();
567 assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
568
569 response = webClient().path("Users").path(user.getId()).get();
570 assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
571 }
572
573 @Test
574 public void createGroup() {
575 String displayName = UUID.randomUUID().toString();
576
577 SCIMGroup group = new SCIMGroup(null, null, displayName);
578 group.getMembers().add(new Member("1417acbe-cbf6-4277-9372-e75e04f97000", null, null));
579 assertNull(group.getId());
580 assertEquals(displayName, group.getDisplayName());
581
582 Response response = webClient().path("Groups").post(group);
583 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
584
585 group = response.readEntity(SCIMGroup.class);
586 assertNotNull(group.getId());
587 assertTrue(response.getLocation().toASCIIString().endsWith(group.getId()));
588 assertEquals(1, group.getMembers().size());
589 assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", group.getMembers().get(0).getValue());
590
591 response = webClient().path("Users").path("1417acbe-cbf6-4277-9372-e75e04f97000").get();
592 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
593
594 SCIMUser user = response.readEntity(SCIMUser.class);
595 assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", user.getId());
596
597 response = webClient().path("Groups").post(group);
598 assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
599
600 SCIMError error = response.readEntity(SCIMError.class);
601 assertEquals(Response.Status.CONFLICT.getStatusCode(), error.getStatus());
602 assertEquals(ErrorType.uniqueness, error.getScimType());
603 }
604
605 @Test
606 public void updateGroup() {
607 SCIM_CONF_SERVICE.set(CONF);
608
609 SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
610 group.getMembers().add(new Member("74cd8ece-715a-44a4-a736-e17b46c4e7e6", null, null));
611 group.getMembers().add(new Member("1417acbe-cbf6-4277-9372-e75e04f97000", null, null));
612 Response response = webClient().path("Groups").post(group);
613 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
614
615 group = response.readEntity(SCIMGroup.class);
616 assertNotNull(group.getId());
617 assertNull(group.getExternalId());
618 assertEquals(2, group.getMembers().size());
619
620
621 String body =
622 "{"
623 + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
624 + "\"Operations\":[{"
625 + "\"op\":\"Add\","
626 + "\"path\":\"externalId\","
627 + "\"value\":\"" + group.getId() + "\""
628 + "}]"
629 + "}";
630 response = webClient().path("Groups").path(group.getId()).invoke(HttpMethod.PATCH, body);
631 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
632
633 group = response.readEntity(SCIMGroup.class);
634 assertEquals(group.getId(), group.getExternalId());
635
636
637 body =
638 "{"
639 + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
640 + "\"Operations\":["
641 + "{"
642 + "\"op\":\"Add\","
643 + "\"path\":\"members\","
644 + "\"value\":[{"
645 + "\"value\":\"b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee\"}]"
646 + "},"
647 + "{"
648 + "\"op\":\"remove\","
649 + "\"path\":\"members[value eq \\\"74cd8ece-715a-44a4-a736-e17b46c4e7e6\\\"]\""
650 + "},"
651 + "{"
652 + "\"op\":\"remove\","
653 + "\"path\":\"externalId\""
654 + "}"
655 + "]"
656 + "}";
657 response = webClient().path("Groups").path(group.getId()).invoke(HttpMethod.PATCH, body);
658 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
659
660 group = response.readEntity(SCIMGroup.class);
661 assertEquals(2, group.getMembers().size());
662 assertTrue(group.getMembers().stream().
663 anyMatch(m -> "b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee".equals(m.getValue())));
664 assertTrue(group.getMembers().stream().
665 anyMatch(m -> "1417acbe-cbf6-4277-9372-e75e04f97000".equals(m.getValue())));
666 assertNull(group.getExternalId());
667
668
669 body =
670 "{"
671 + "\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],"
672 + "\"Operations\":["
673 + "{"
674 + "\"op\":\"remove\","
675 + "\"path\":\"members\""
676 + "}"
677 + "]"
678 + "}";
679 response = webClient().path("Groups").path(group.getId()).invoke(HttpMethod.PATCH, body);
680 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
681
682 group = response.readEntity(SCIMGroup.class);
683 assertTrue(group.getMembers().isEmpty());
684 }
685
686 @Test
687 public void replaceGroup() {
688 SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
689 group.getMembers().add(new Member("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee", null, null));
690 Response response = webClient().path("Groups").post(group);
691 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
692
693 group = response.readEntity(SCIMGroup.class);
694 assertNotNull(group.getId());
695 assertEquals(1, group.getMembers().size());
696 assertEquals("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee", group.getMembers().get(0).getValue());
697
698 group.setDisplayName("other" + group.getId());
699 group.getMembers().add(new Member("c9b2dec2-00a7-4855-97c0-d854842b4b24", null, null));
700
701 response = webClient().path("Groups").path(group.getId()).put(group);
702 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
703
704 group = response.readEntity(SCIMGroup.class);
705 assertTrue(group.getDisplayName().startsWith("other"));
706 assertEquals(2, group.getMembers().size());
707
708 group.getMembers().clear();
709 group.getMembers().add(new Member("c9b2dec2-00a7-4855-97c0-d854842b4b24", null, null));
710
711 response = webClient().path("Groups").path(group.getId()).put(group);
712 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
713
714 group = response.readEntity(SCIMGroup.class);
715 assertEquals(1, group.getMembers().size());
716 assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", group.getMembers().get(0).getValue());
717 }
718
719 @Test
720 public void deleteGroup() {
721 SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
722 Response response = webClient().path("Groups").post(group);
723 assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
724
725 group = response.readEntity(SCIMGroup.class);
726 assertNotNull(group.getId());
727
728 response = webClient().path("Groups").path(group.getId()).get();
729 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
730
731 response = webClient().path("Groups").path(group.getId()).delete();
732 assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
733
734 response = webClient().path("Groups").path(group.getId()).get();
735 assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
736 }
737 }