1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.logic;
20
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28 import org.apache.commons.jexl3.MapContext;
29 import org.apache.commons.lang3.BooleanUtils;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.commons.lang3.tuple.Pair;
32 import org.apache.syncope.common.lib.AnyOperations;
33 import org.apache.syncope.common.lib.Attr;
34 import org.apache.syncope.common.lib.EntityTOUtils;
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.PasswordPatch;
40 import org.apache.syncope.common.lib.request.StatusR;
41 import org.apache.syncope.common.lib.request.StringReplacePatchItem;
42 import org.apache.syncope.common.lib.request.UserCR;
43 import org.apache.syncope.common.lib.request.UserUR;
44 import org.apache.syncope.common.lib.scim.SCIMComplexConf;
45 import org.apache.syncope.common.lib.scim.SCIMConf;
46 import org.apache.syncope.common.lib.scim.SCIMEnterpriseUserConf;
47 import org.apache.syncope.common.lib.scim.SCIMManagerConf;
48 import org.apache.syncope.common.lib.scim.SCIMUserAddressConf;
49 import org.apache.syncope.common.lib.to.GroupTO;
50 import org.apache.syncope.common.lib.to.MembershipTO;
51 import org.apache.syncope.common.lib.to.UserTO;
52 import org.apache.syncope.common.lib.types.PatchOperation;
53 import org.apache.syncope.common.lib.types.StatusRType;
54 import org.apache.syncope.core.logic.scim.SCIMConfManager;
55 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
56 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
57 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
58 import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
59 import org.apache.syncope.core.spring.security.AuthDataAccessor;
60 import org.apache.syncope.ext.scimv2.api.BadRequestException;
61 import org.apache.syncope.ext.scimv2.api.data.Group;
62 import org.apache.syncope.ext.scimv2.api.data.Member;
63 import org.apache.syncope.ext.scimv2.api.data.Meta;
64 import org.apache.syncope.ext.scimv2.api.data.SCIMComplexValue;
65 import org.apache.syncope.ext.scimv2.api.data.SCIMEnterpriseInfo;
66 import org.apache.syncope.ext.scimv2.api.data.SCIMGroup;
67 import org.apache.syncope.ext.scimv2.api.data.SCIMPatchOperation;
68 import org.apache.syncope.ext.scimv2.api.data.SCIMUser;
69 import org.apache.syncope.ext.scimv2.api.data.SCIMUserAddress;
70 import org.apache.syncope.ext.scimv2.api.data.SCIMUserManager;
71 import org.apache.syncope.ext.scimv2.api.data.SCIMUserName;
72 import org.apache.syncope.ext.scimv2.api.data.Value;
73 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
74 import org.apache.syncope.ext.scimv2.api.type.Function;
75 import org.apache.syncope.ext.scimv2.api.type.PatchOp;
76 import org.apache.syncope.ext.scimv2.api.type.Resource;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79 import org.springframework.util.CollectionUtils;
80
81 public class SCIMDataBinder {
82
83 protected static final Logger LOG = LoggerFactory.getLogger(SCIMDataBinder.class);
84
85 protected static final List<String> USER_SCHEMAS = List.of(Resource.User.schema());
86
87 protected static final List<String> ENTERPRISE_USER_SCHEMAS =
88 List.of(Resource.User.schema(), Resource.EnterpriseUser.schema());
89
90 protected static final List<String> GROUP_SCHEMAS = List.of(Resource.Group.schema());
91
92
93
94
95
96
97
98 public static String filter2JexlExpression(final String filter) {
99 String jexlExpression = filter.
100 replace(" co ", " =~ ").
101 replace(" sw ", " =^ ").
102 replace(" ew ", " =$ ");
103
104 boolean endsWithPR = jexlExpression.endsWith(" pr");
105 int pr = endsWithPR ? jexlExpression.indexOf(" pr") : jexlExpression.indexOf(" pr ");
106 while (pr != -1) {
107 String before = jexlExpression.substring(0, pr);
108 int start = before.indexOf(' ') == -1 ? 0 : jexlExpression.substring(0, pr).lastIndexOf(' ', pr) + 1;
109 String literal = jexlExpression.substring(start, pr);
110
111 endsWithPR = jexlExpression.endsWith(" pr");
112 jexlExpression = jexlExpression.replace(
113 literal + " pr" + (endsWithPR ? "" : " "),
114 "not(empty(" + literal + "))" + (endsWithPR ? "" : " "));
115
116 pr = endsWithPR ? jexlExpression.indexOf(" pr") : jexlExpression.indexOf(" pr ");
117 }
118
119 return jexlExpression;
120 }
121
122 protected final SCIMConfManager confManager;
123
124 protected final UserLogic userLogic;
125
126 protected final AuthDataAccessor authDataAccessor;
127
128 public SCIMDataBinder(
129 final SCIMConfManager confManager,
130 final UserLogic userLogic,
131 final AuthDataAccessor authDataAccessor) {
132
133 this.confManager = confManager;
134 this.userLogic = userLogic;
135 this.authDataAccessor = authDataAccessor;
136 }
137
138 protected <E extends Enum<?>> void fill(
139 final Map<String, Attr> attrs,
140 final List<SCIMComplexConf<E>> confs,
141 final List<SCIMComplexValue> values) {
142
143 confs.forEach(conf -> {
144 SCIMComplexValue value = new SCIMComplexValue();
145
146 if (conf.getValue() != null && attrs.containsKey(conf.getValue())) {
147 value.setValue(attrs.get(conf.getValue()).getValues().get(0));
148 }
149 if (conf.getDisplay() != null && attrs.containsKey(conf.getDisplay())) {
150 value.setDisplay(attrs.get(conf.getDisplay()).getValues().get(0));
151 }
152 if (conf.getType() != null) {
153 value.setType(conf.getType().name());
154 }
155
156 value.setPrimary(conf.isPrimary());
157
158 if (!value.isEmpty()) {
159 values.add(value);
160 }
161 });
162 }
163
164 protected boolean output(
165 final List<String> attributes,
166 final List<String> excludedAttributes,
167 final String schema) {
168
169 return (attributes.isEmpty() || attributes.contains(schema))
170 && (excludedAttributes.isEmpty() || !excludedAttributes.contains(schema));
171 }
172
173 protected <T> T output(
174 final List<String> attributes,
175 final List<String> excludedAttributes,
176 final String schema,
177 final T value) {
178
179 return output(attributes, excludedAttributes, schema)
180 ? value
181 : null;
182 }
183
184 public SCIMUser toSCIMUser(
185 final UserTO userTO,
186 final String location,
187 final List<String> attributes,
188 final List<String> excludedAttributes) {
189
190 SCIMConf conf = confManager.get();
191
192 List<String> schemas = new ArrayList<>();
193 schemas.add(Resource.User.schema());
194 if (conf.getEnterpriseUserConf() != null) {
195 schemas.add(Resource.EnterpriseUser.schema());
196 }
197
198 SCIMUser user = new SCIMUser(
199 userTO.getKey(),
200 schemas,
201 new Meta(
202 Resource.User,
203 userTO.getCreationDate(),
204 Optional.ofNullable(userTO.getLastChangeDate()).orElse(userTO.getCreationDate()),
205 userTO.getETagValue(),
206 location),
207 output(attributes, excludedAttributes, "userName", userTO.getUsername()),
208 !userTO.isSuspended());
209
210 Map<String, Attr> attrs = new HashMap<>();
211 attrs.putAll(EntityTOUtils.buildAttrMap(userTO.getPlainAttrs()));
212 attrs.putAll(EntityTOUtils.buildAttrMap(userTO.getDerAttrs()));
213 attrs.putAll(EntityTOUtils.buildAttrMap(userTO.getVirAttrs()));
214 attrs.put("username", new Attr.Builder("username").value(userTO.getUsername()).build());
215
216 if (conf.getUserConf() != null) {
217 if (output(attributes, excludedAttributes, "externalId")
218 && conf.getUserConf().getExternalId() != null
219 && attrs.containsKey(conf.getUserConf().getExternalId())) {
220
221 user.setExternalId(attrs.get(conf.getUserConf().getExternalId()).getValues().get(0));
222 }
223
224 if (output(attributes, excludedAttributes, "name") && conf.getUserConf().getName() != null) {
225 SCIMUserName name = new SCIMUserName();
226
227 if (conf.getUserConf().getName().getFamilyName() != null
228 && attrs.containsKey(conf.getUserConf().getName().getFamilyName())) {
229
230 name.setFamilyName(attrs.get(conf.getUserConf().getName().getFamilyName()).getValues().get(0));
231 }
232 if (conf.getUserConf().getName().getFormatted() != null
233 && attrs.containsKey(conf.getUserConf().getName().getFormatted())) {
234
235 name.setFormatted(attrs.get(conf.getUserConf().getName().getFormatted()).getValues().get(0));
236 }
237 if (conf.getUserConf().getName().getGivenName() != null
238 && attrs.containsKey(conf.getUserConf().getName().getGivenName())) {
239
240 name.setGivenName(attrs.get(conf.getUserConf().getName().getGivenName()).getValues().get(0));
241 }
242 if (conf.getUserConf().getName().getHonorificPrefix() != null
243 && attrs.containsKey(conf.getUserConf().getName().getHonorificPrefix())) {
244
245 name.setHonorificPrefix(
246 attrs.get(conf.getUserConf().getName().getHonorificPrefix()).getValues().get(0));
247 }
248 if (conf.getUserConf().getName().getHonorificSuffix() != null
249 && attrs.containsKey(conf.getUserConf().getName().getHonorificSuffix())) {
250
251 name.setHonorificSuffix(
252 attrs.get(conf.getUserConf().getName().getHonorificSuffix()).getValues().get(0));
253 }
254 if (conf.getUserConf().getName().getMiddleName() != null
255 && attrs.containsKey(conf.getUserConf().getName().getMiddleName())) {
256
257 name.setMiddleName(attrs.get(conf.getUserConf().getName().getMiddleName()).getValues().get(0));
258 }
259
260 if (!name.isEmpty()) {
261 user.setName(name);
262 }
263 }
264
265 if (output(attributes, excludedAttributes, "displayName")
266 && conf.getUserConf().getDisplayName() != null
267 && attrs.containsKey(conf.getUserConf().getDisplayName())) {
268
269 user.setDisplayName(attrs.get(conf.getUserConf().getDisplayName()).getValues().get(0));
270 }
271 if (output(attributes, excludedAttributes, "nickName")
272 && conf.getUserConf().getNickName() != null
273 && attrs.containsKey(conf.getUserConf().getNickName())) {
274
275 user.setNickName(attrs.get(conf.getUserConf().getNickName()).getValues().get(0));
276 }
277 if (output(attributes, excludedAttributes, "profileUrl")
278 && conf.getUserConf().getProfileUrl() != null
279 && attrs.containsKey(conf.getUserConf().getProfileUrl())) {
280
281 user.setProfileUrl(attrs.get(conf.getUserConf().getProfileUrl()).getValues().get(0));
282 }
283 if (output(attributes, excludedAttributes, "title")
284 && conf.getUserConf().getTitle() != null
285 && attrs.containsKey(conf.getUserConf().getTitle())) {
286
287 user.setTitle(attrs.get(conf.getUserConf().getTitle()).getValues().get(0));
288 }
289 if (output(attributes, excludedAttributes, "userType")
290 && conf.getUserConf().getUserType() != null
291 && attrs.containsKey(conf.getUserConf().getUserType())) {
292
293 user.setUserType(attrs.get(conf.getUserConf().getUserType()).getValues().get(0));
294 }
295 if (output(attributes, excludedAttributes, "preferredLanguage")
296 && conf.getUserConf().getPreferredLanguage() != null
297 && attrs.containsKey(conf.getUserConf().getPreferredLanguage())) {
298
299 user.setPreferredLanguage(attrs.get(conf.getUserConf().getPreferredLanguage()).getValues().get(0));
300 }
301 if (output(attributes, excludedAttributes, "locale")
302 && conf.getUserConf().getLocale() != null
303 && attrs.containsKey(conf.getUserConf().getLocale())) {
304
305 user.setLocale(attrs.get(conf.getUserConf().getLocale()).getValues().get(0));
306 }
307 if (output(attributes, excludedAttributes, "timezone")
308 && conf.getUserConf().getTimezone() != null
309 && attrs.containsKey(conf.getUserConf().getTimezone())) {
310
311 user.setTimezone(attrs.get(conf.getUserConf().getTimezone()).getValues().get(0));
312 }
313
314 if (output(attributes, excludedAttributes, "emails")) {
315 fill(attrs, conf.getUserConf().getEmails(), user.getEmails());
316 }
317 if (output(attributes, excludedAttributes, "phoneNumbers")) {
318 fill(attrs, conf.getUserConf().getPhoneNumbers(), user.getPhoneNumbers());
319 }
320 if (output(attributes, excludedAttributes, "ims")) {
321 fill(attrs, conf.getUserConf().getIms(), user.getIms());
322 }
323 if (output(attributes, excludedAttributes, "photos")) {
324 fill(attrs, conf.getUserConf().getPhotos(), user.getPhotos());
325 }
326 if (output(attributes, excludedAttributes, "addresses")) {
327 conf.getUserConf().getAddresses().forEach(addressConf -> {
328 SCIMUserAddress address = new SCIMUserAddress();
329
330 if (addressConf.getFormatted() != null && attrs.containsKey(addressConf.getFormatted())) {
331 address.setFormatted(attrs.get(addressConf.getFormatted()).getValues().get(0));
332 }
333 if (addressConf.getStreetAddress() != null && attrs.containsKey(addressConf.getStreetAddress())) {
334 address.setStreetAddress(attrs.get(addressConf.getStreetAddress()).getValues().get(0));
335 }
336 if (addressConf.getLocality() != null && attrs.containsKey(addressConf.getLocality())) {
337 address.setLocality(attrs.get(addressConf.getLocality()).getValues().get(0));
338 }
339 if (addressConf.getRegion() != null && attrs.containsKey(addressConf.getRegion())) {
340 address.setRegion(attrs.get(addressConf.getRegion()).getValues().get(0));
341 }
342 if (addressConf.getCountry() != null && attrs.containsKey(addressConf.getCountry())) {
343 address.setCountry(attrs.get(addressConf.getCountry()).getValues().get(0));
344 }
345 if (addressConf.getType() != null) {
346 address.setType(addressConf.getType().name());
347 }
348 if (addressConf.isPrimary()) {
349 address.setPrimary(true);
350 }
351
352 if (!address.isEmpty()) {
353 user.getAddresses().add(address);
354 }
355 });
356 }
357 if (output(attributes, excludedAttributes, "x509Certificates")) {
358 conf.getUserConf().getX509Certificates().stream().filter(attrs::containsKey).
359 forEach(cert -> user.getX509Certificates().add(new Value(attrs.get(cert).getValues().get(0))));
360 }
361 }
362
363 if (conf.getEnterpriseUserConf() != null) {
364 SCIMEnterpriseInfo enterpriseInfo = new SCIMEnterpriseInfo();
365
366 if (output(attributes, excludedAttributes, "employeeNumber")
367 && conf.getEnterpriseUserConf().getEmployeeNumber() != null
368 && attrs.containsKey(conf.getEnterpriseUserConf().getEmployeeNumber())) {
369
370 enterpriseInfo.setEmployeeNumber(
371 attrs.get(conf.getEnterpriseUserConf().getEmployeeNumber()).getValues().get(0));
372 }
373 if (output(attributes, excludedAttributes, "costCenter")
374 && conf.getEnterpriseUserConf().getCostCenter() != null
375 && attrs.containsKey(conf.getEnterpriseUserConf().getCostCenter())) {
376
377 enterpriseInfo.setCostCenter(
378 attrs.get(conf.getEnterpriseUserConf().getCostCenter()).getValues().get(0));
379 }
380 if (output(attributes, excludedAttributes, "organization")
381 && conf.getEnterpriseUserConf().getOrganization() != null
382 && attrs.containsKey(conf.getEnterpriseUserConf().getOrganization())) {
383
384 enterpriseInfo.setOrganization(
385 attrs.get(conf.getEnterpriseUserConf().getOrganization()).getValues().get(0));
386 }
387 if (output(attributes, excludedAttributes, "division")
388 && conf.getEnterpriseUserConf().getDivision() != null
389 && attrs.containsKey(conf.getEnterpriseUserConf().getDivision())) {
390
391 enterpriseInfo.setDivision(
392 attrs.get(conf.getEnterpriseUserConf().getDivision()).getValues().get(0));
393 }
394 if (output(attributes, excludedAttributes, "department")
395 && conf.getEnterpriseUserConf().getDepartment() != null
396 && attrs.containsKey(conf.getEnterpriseUserConf().getDepartment())) {
397
398 enterpriseInfo.setDepartment(
399 attrs.get(conf.getEnterpriseUserConf().getDepartment()).getValues().get(0));
400 }
401 if (output(attributes, excludedAttributes, "manager")
402 && conf.getEnterpriseUserConf().getManager() != null) {
403
404 SCIMUserManager manager = new SCIMUserManager();
405
406 if (conf.getEnterpriseUserConf().getManager().getKey() != null
407 && attrs.containsKey(conf.getEnterpriseUserConf().getManager().getKey())) {
408
409 try {
410 UserTO userManager = userLogic.read(attrs.get(
411 conf.getEnterpriseUserConf().getManager().getKey()).getValues().get(0));
412 manager.setValue(userManager.getKey());
413 manager.setRef(
414 StringUtils.substringBefore(location, "/Users") + "/Users/" + userManager.getKey());
415
416 if (conf.getEnterpriseUserConf().getManager().getDisplayName() != null) {
417 Attr displayName = userManager.getPlainAttr(
418 conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
419 if (displayName == null) {
420 displayName = userManager.getDerAttr(
421 conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
422 }
423 if (displayName == null) {
424 displayName = userManager.getVirAttr(
425 conf.getEnterpriseUserConf().getManager().getDisplayName()).orElse(null);
426 }
427 if (displayName != null) {
428 manager.setDisplayName(displayName.getValues().get(0));
429 }
430 }
431 } catch (Exception e) {
432 LOG.error("Could not read user {}", conf.getEnterpriseUserConf().getManager().getKey(), e);
433 }
434 }
435
436 if (!manager.isEmpty()) {
437 enterpriseInfo.setManager(manager);
438 }
439 }
440
441 if (!enterpriseInfo.isEmpty()) {
442 user.setEnterpriseInfo(enterpriseInfo);
443 }
444 }
445
446 if (output(attributes, excludedAttributes, "groups")) {
447 userTO.getMemberships().forEach(membership -> user.getGroups().add(new Group(
448 membership.getGroupKey(),
449 StringUtils.substringBefore(location, "/Users") + "/Groups/" + membership.getGroupKey(),
450 membership.getGroupName(),
451 Function.direct)));
452 userTO.getDynMemberships().forEach(membership -> user.getGroups().add(new Group(
453 membership.getGroupKey(),
454 StringUtils.substringBefore(location, "/Users") + "/Groups/" + membership.getGroupKey(),
455 membership.getGroupName(),
456 Function.indirect)));
457 }
458
459 if (output(attributes, excludedAttributes, "entitlements")) {
460 authDataAccessor.getAuthorities(userTO.getUsername(), null).forEach(authority -> user.getEntitlements().
461 add(new Value(authority.getAuthority() + " on Realm(s) " + authority.getRealms())));
462 }
463
464 if (output(attributes, excludedAttributes, "roles")) {
465 userTO.getRoles().forEach(role -> user.getRoles().add(new Value(role)));
466 }
467
468 return user;
469 }
470
471 protected void setAttribute(
472 final UserTO userTO,
473 final String schema,
474 final String value) {
475
476 if (schema == null || value == null) {
477 return;
478 }
479
480 switch (schema) {
481 case "username":
482 userTO.setUsername(value);
483 break;
484
485 default:
486 userTO.getPlainAttrs().add(new Attr.Builder(schema).value(value).build());
487 }
488 }
489
490 protected <E extends Enum<?>> void setAttribute(
491 final Set<Attr> attrs,
492 final List<SCIMComplexConf<E>> confs,
493 final List<SCIMComplexValue> values) {
494
495 values.stream().filter(value -> value.getType() != null).forEach(value -> confs.stream().
496 filter(object -> value.getType().equals(object.getType().name())).findFirst().
497 ifPresent(conf -> attrs.add(
498 new Attr.Builder(conf.getValue()).value(value.getValue()).build())));
499 }
500
501 public UserTO toUserTO(final SCIMUser user, final boolean checkSchemas) {
502 if (checkSchemas
503 && !USER_SCHEMAS.equals(user.getSchemas())
504 && !ENTERPRISE_USER_SCHEMAS.equals(user.getSchemas())) {
505
506 throw new BadRequestException(ErrorType.invalidValue);
507 }
508
509 UserTO userTO = new UserTO();
510 userTO.setRealm(SyncopeConstants.ROOT_REALM);
511 userTO.setKey(user.getId());
512 userTO.setPassword(user.getPassword());
513 userTO.setUsername(user.getUserName());
514
515 SCIMConf conf = confManager.get();
516
517 if (conf.getUserConf() != null) {
518 setAttribute(
519 userTO,
520 conf.getUserConf().getExternalId(),
521 user.getExternalId());
522
523 if (conf.getUserConf().getName() != null && user.getName() != null) {
524 setAttribute(
525 userTO,
526 conf.getUserConf().getName().getFamilyName(),
527 user.getName().getFamilyName());
528
529 setAttribute(
530 userTO,
531 conf.getUserConf().getName().getFormatted(),
532 user.getName().getFormatted());
533
534 setAttribute(
535 userTO,
536 conf.getUserConf().getName().getGivenName(),
537 user.getName().getGivenName());
538
539 setAttribute(
540 userTO,
541 conf.getUserConf().getName().getHonorificPrefix(),
542 user.getName().getHonorificPrefix());
543
544 setAttribute(
545 userTO,
546 conf.getUserConf().getName().getHonorificSuffix(),
547 user.getName().getHonorificSuffix());
548
549 setAttribute(
550 userTO,
551 conf.getUserConf().getName().getMiddleName(),
552 user.getName().getMiddleName());
553 }
554
555 setAttribute(
556 userTO,
557 conf.getUserConf().getDisplayName(),
558 user.getDisplayName());
559
560 setAttribute(
561 userTO,
562 conf.getUserConf().getNickName(),
563 user.getNickName());
564
565 setAttribute(
566 userTO,
567 conf.getUserConf().getProfileUrl(),
568 user.getProfileUrl());
569
570 setAttribute(
571 userTO,
572 conf.getUserConf().getTitle(),
573 user.getTitle());
574
575 setAttribute(
576 userTO,
577 conf.getUserConf().getUserType(),
578 user.getUserType());
579
580 setAttribute(
581 userTO,
582 conf.getUserConf().getPreferredLanguage(),
583 user.getPreferredLanguage());
584
585 setAttribute(
586 userTO,
587 conf.getUserConf().getLocale(),
588 user.getLocale());
589
590 setAttribute(
591 userTO,
592 conf.getUserConf().getTimezone(),
593 user.getTimezone());
594
595 setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getEmails(), user.getEmails());
596 setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getPhoneNumbers(), user.getPhoneNumbers());
597 setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getIms(), user.getIms());
598 setAttribute(userTO.getPlainAttrs(), conf.getUserConf().getPhotos(), user.getPhotos());
599
600 user.getAddresses().stream().filter(address -> address.getType() != null).
601 forEach(address -> conf.getUserConf().getAddresses().stream().
602 filter(object -> address.getType().equals(object.getType().name())).findFirst().
603 ifPresent(addressConf -> {
604 setAttribute(
605 userTO,
606 addressConf.getFormatted(),
607 address.getFormatted());
608
609 setAttribute(
610 userTO,
611 addressConf.getStreetAddress(),
612 address.getStreetAddress());
613
614 setAttribute(
615 userTO,
616 addressConf.getLocality(),
617 address.getLocality());
618
619 setAttribute(
620 userTO,
621 addressConf.getRegion(),
622 address.getRegion());
623
624 setAttribute(
625 userTO,
626 addressConf.getPostalCode(),
627 address.getPostalCode());
628
629 setAttribute(
630 userTO,
631 addressConf.getCountry(),
632 address.getCountry());
633 }));
634
635 for (int i = 0; i < user.getX509Certificates().size(); i++) {
636 Value certificate = user.getX509Certificates().get(i);
637 if (conf.getUserConf().getX509Certificates().size() > i) {
638 setAttribute(
639 userTO,
640 conf.getUserConf().getX509Certificates().get(i),
641 certificate.getValue());
642 }
643 }
644 }
645
646 if (conf.getEnterpriseUserConf() != null && user.getEnterpriseInfo() != null) {
647 setAttribute(
648 userTO,
649 conf.getEnterpriseUserConf().getEmployeeNumber(),
650 user.getEnterpriseInfo().getEmployeeNumber());
651
652 setAttribute(
653 userTO,
654 conf.getEnterpriseUserConf().getCostCenter(),
655 user.getEnterpriseInfo().getCostCenter());
656
657 setAttribute(
658 userTO,
659 conf.getEnterpriseUserConf().getOrganization(),
660 user.getEnterpriseInfo().getOrganization());
661
662 setAttribute(
663 userTO,
664 conf.getEnterpriseUserConf().getDivision(),
665 user.getEnterpriseInfo().getDivision());
666
667 setAttribute(
668 userTO,
669 conf.getEnterpriseUserConf().getDepartment(),
670 user.getEnterpriseInfo().getDepartment());
671
672 setAttribute(
673 userTO,
674 Optional.ofNullable(conf.getEnterpriseUserConf().getManager()).
675 map(SCIMManagerConf::getKey).orElse(null),
676 Optional.ofNullable(user.getEnterpriseInfo().getManager()).
677 map(SCIMUserManager::getValue).orElse(null));
678 }
679
680 userTO.getMemberships().addAll(user.getGroups().stream().
681 map(group -> new MembershipTO.Builder(group.getValue()).build()).
682 collect(Collectors.toList()));
683
684 userTO.getRoles().addAll(user.getRoles().stream().
685 map(Value::getValue).
686 collect(Collectors.toList()));
687
688 return userTO;
689 }
690
691 public UserCR toUserCR(final SCIMUser user) {
692 UserTO userTO = toUserTO(user, true);
693 UserCR userCR = new UserCR();
694 EntityTOUtils.toAnyCR(userTO, userCR);
695 return userCR;
696 }
697
698 protected void setAttribute(final Set<AttrPatch> attrs, final String schema, final SCIMPatchOperation op) {
699 Optional.ofNullable(schema).ifPresent(a -> {
700 Attr.Builder attr = new Attr.Builder(a);
701 if (!CollectionUtils.isEmpty(op.getValue())) {
702 attr.value(op.getValue().get(0).toString());
703 }
704
705 attrs.add(new AttrPatch.Builder(attr.build()).
706 operation(op.getOp() == PatchOp.remove ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
707 build());
708 });
709 }
710
711 protected <E extends Enum<?>> void setAttribute(
712 final Set<AttrPatch> attrs,
713 final List<SCIMComplexConf<E>> confs,
714 final SCIMPatchOperation op) {
715
716 confs.stream().
717 filter(conf -> BooleanUtils.toBoolean(JexlUtils.evaluate(
718 filter2JexlExpression(op.getPath().getFilter()),
719 new MapContext(Map.of("type", conf.getType().name()))).toString())).findFirst().
720 ifPresent(conf -> {
721 if (op.getPath().getSub() == null || "display".equals(op.getPath().getSub())) {
722 setAttribute(attrs, conf.getDisplay(), op);
723 }
724 if (op.getPath().getSub() == null || "value".equals(op.getPath().getSub())) {
725 setAttribute(attrs, conf.getValue(), op);
726 }
727 });
728 }
729
730 protected <E extends Enum<?>> void setAttribute(
731 final Set<AttrPatch> attrs,
732 final List<SCIMComplexConf<E>> confs,
733 final List<SCIMComplexValue> values,
734 final PatchOp patchOp) {
735
736 values.stream().
737 filter(value -> value.getType() != null).forEach(value -> confs.stream().
738 filter(conf -> value.getType().equals(conf.getType().name())).findFirst().
739 ifPresent(conf -> attrs.add(new AttrPatch.Builder(
740 new Attr.Builder(conf.getValue()).value(value.getValue()).build()).
741 operation(patchOp == PatchOp.remove ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
742 build())));
743 }
744
745 protected void setAttribute(
746 final Set<AttrPatch> attrs,
747 final SCIMUserAddressConf conf,
748 final SCIMPatchOperation op) {
749
750 if (op.getPath().getSub() == null || "formatted".equals(op.getPath().getSub())) {
751 setAttribute(attrs, conf.getFormatted(), op);
752 }
753 if (op.getPath().getSub() == null || "streetAddress".equals(op.getPath().getSub())) {
754 setAttribute(attrs, conf.getStreetAddress(), op);
755 }
756 if (op.getPath().getSub() == null || "locality".equals(op.getPath().getSub())) {
757 setAttribute(attrs, conf.getLocality(), op);
758 }
759 if (op.getPath().getSub() == null || "region".equals(op.getPath().getSub())) {
760 setAttribute(attrs, conf.getRegion(), op);
761 }
762 if (op.getPath().getSub() == null || "postalCode".equals(op.getPath().getSub())) {
763 setAttribute(attrs, conf.getPostalCode(), op);
764 }
765 if (op.getPath().getSub() == null || "country".equals(op.getPath().getSub())) {
766 setAttribute(attrs, conf.getCountry(), op);
767 }
768 }
769
770 public Pair<UserUR, StatusR> toUserUpdate(final UserTO before, final SCIMPatchOperation op) {
771 StatusR statusR = null;
772
773 if (op.getPath() == null && op.getOp() != PatchOp.remove
774 && !CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
775
776 SCIMUser after = (SCIMUser) op.getValue().get(0);
777
778 if (after.getActive() != null && before.isSuspended() == after.isActive()) {
779 statusR = new StatusR.Builder(
780 before.getKey(),
781 after.isActive() ? StatusRType.REACTIVATE : StatusRType.SUSPEND).
782 build();
783 }
784
785 UserTO updated = toUserTO(after, false);
786 updated.setKey(before.getKey());
787 return Pair.of(AnyOperations.diff(updated, before, true), statusR);
788 }
789
790 UserUR userUR = new UserUR.Builder(before.getKey()).build();
791
792 SCIMConf conf = confManager.get();
793 if (conf == null) {
794 return Pair.of(userUR, statusR);
795 }
796
797 switch (op.getPath().getAttribute()) {
798 case "externalId":
799 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getExternalId(), op);
800 break;
801
802 case "userName":
803 if (op.getOp() != PatchOp.remove && !CollectionUtils.isEmpty(op.getValue())) {
804 userUR.setUsername(new StringReplacePatchItem.Builder().
805 value(op.getValue().get(0).toString()).build());
806 }
807 break;
808
809 case "password":
810 if (op.getOp() != PatchOp.remove && !CollectionUtils.isEmpty(op.getValue())) {
811 userUR.setPassword(new PasswordPatch.Builder().
812 value(op.getValue().get(0).toString()).build());
813 }
814 break;
815
816 case "active":
817 if (!CollectionUtils.isEmpty(op.getValue())) {
818
819
820 if (op.getValue().get(0) instanceof String) {
821 String a = (String) op.getValue().get(0);
822 op.setValue(List.of(BooleanUtils.toBoolean(a)));
823 }
824
825 statusR = new StatusR.Builder(
826 before.getKey(),
827 (boolean) op.getValue().get(0) ? StatusRType.REACTIVATE : StatusRType.SUSPEND).
828 build();
829 }
830 break;
831
832 case "name":
833 if (conf.getUserConf().getName() != null) {
834 if (op.getPath().getSub() == null || "familyName".equals(op.getPath().getSub())) {
835 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getFamilyName(), op);
836 }
837 if (op.getPath().getSub() == null || "formatted".equals(op.getPath().getSub())) {
838 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getFormatted(), op);
839 }
840 if (op.getPath().getSub() == null || "givenName".equals(op.getPath().getSub())) {
841 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getGivenName(), op);
842 }
843 if (op.getPath().getSub() == null || "honorificPrefix".equals(op.getPath().getSub())) {
844 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getHonorificPrefix(), op);
845 }
846 if (op.getPath().getSub() == null || "honorificSuffix".equals(op.getPath().getSub())) {
847 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getHonorificSuffix(), op);
848 }
849 if (op.getPath().getSub() == null || "middleName".equals(op.getPath().getSub())) {
850 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getName().getMiddleName(), op);
851 }
852 }
853 break;
854
855 case "displayName":
856 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getDisplayName(), op);
857 break;
858
859 case "nickName":
860 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getNickName(), op);
861 break;
862
863 case "profileUrl":
864 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getProfileUrl(), op);
865 break;
866
867 case "title":
868 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getTitle(), op);
869 break;
870
871 case "userType":
872 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getUserType(), op);
873 break;
874
875 case "preferredLanguage":
876 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getPreferredLanguage(), op);
877 break;
878
879 case "locale":
880 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getLocale(), op);
881 break;
882
883 case "timezone":
884 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getTimezone(), op);
885 break;
886
887 case "emails":
888 if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
889 setAttribute(
890 userUR.getPlainAttrs(),
891 conf.getUserConf().getEmails(),
892 ((SCIMUser) op.getValue().get(0)).getEmails(),
893 op.getOp());
894 } else if (op.getPath().getFilter() != null) {
895 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getEmails(), op);
896 }
897 break;
898
899 case "phoneNumbers":
900 if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
901 setAttribute(
902 userUR.getPlainAttrs(),
903 conf.getUserConf().getPhoneNumbers(),
904 ((SCIMUser) op.getValue().get(0)).getPhoneNumbers(),
905 op.getOp());
906 } else if (op.getPath().getFilter() != null) {
907 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getPhoneNumbers(), op);
908 }
909 break;
910
911 case "ims":
912 if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
913 setAttribute(
914 userUR.getPlainAttrs(),
915 conf.getUserConf().getIms(),
916 ((SCIMUser) op.getValue().get(0)).getIms(),
917 op.getOp());
918 } else if (op.getPath().getFilter() != null) {
919 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getIms(), op);
920 }
921 break;
922
923 case "photos":
924 if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
925 setAttribute(
926 userUR.getPlainAttrs(),
927 conf.getUserConf().getPhotos(),
928 ((SCIMUser) op.getValue().get(0)).getPhotos(),
929 op.getOp());
930 } else if (op.getPath().getFilter() != null) {
931 setAttribute(userUR.getPlainAttrs(), conf.getUserConf().getPhotos(), op);
932 }
933 break;
934
935 case "addresses":
936 if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue().get(0) instanceof SCIMUser) {
937 SCIMUser after = (SCIMUser) op.getValue().get(0);
938 after.getAddresses().stream().filter(address -> address.getType() != null).
939 forEach(address -> conf.getUserConf().getAddresses().stream().
940 filter(object -> address.getType().equals(object.getType().name())).findFirst().
941 ifPresent(addressConf -> setAttribute(userUR.getPlainAttrs(), addressConf, op)));
942 } else if (op.getPath().getFilter() != null) {
943 conf.getUserConf().getAddresses().stream().
944 filter(addressConf -> BooleanUtils.toBoolean(JexlUtils.evaluate(
945 filter2JexlExpression(op.getPath().getFilter()),
946 new MapContext(Map.of("type", addressConf.getType().name()))).toString())).findFirst().
947 ifPresent(addressConf -> setAttribute(userUR.getPlainAttrs(), addressConf, op));
948 }
949 break;
950
951 case "employeeNumber":
952 setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
953 map(SCIMEnterpriseUserConf::getEmployeeNumber).orElse(null), op);
954 break;
955
956 case "costCenter":
957 setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
958 map(SCIMEnterpriseUserConf::getCostCenter).orElse(null), op);
959 break;
960
961 case "organization":
962 setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
963 map(SCIMEnterpriseUserConf::getOrganization).orElse(null), op);
964 break;
965
966 case "division":
967 setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
968 map(SCIMEnterpriseUserConf::getDivision).orElse(null), op);
969 break;
970
971 case "department":
972 setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
973 map(SCIMEnterpriseUserConf::getDepartment).orElse(null), op);
974 break;
975
976 case "manager":
977 setAttribute(userUR.getPlainAttrs(), Optional.ofNullable(conf.getEnterpriseUserConf()).
978 map(SCIMEnterpriseUserConf::getManager).map(SCIMManagerConf::getKey).orElse(null), op);
979 break;
980
981 default:
982 }
983
984 return Pair.of(userUR, statusR);
985 }
986
987 public SCIMGroup toSCIMGroup(
988 final GroupTO groupTO,
989 final String location,
990 final List<String> attributes,
991 final List<String> excludedAttributes) {
992
993 SCIMGroup group = new SCIMGroup(
994 groupTO.getKey(),
995 new Meta(
996 Resource.Group,
997 groupTO.getCreationDate(),
998 Optional.ofNullable(groupTO.getLastChangeDate()).orElse(groupTO.getCreationDate()),
999 groupTO.getETagValue(),
1000 location),
1001 output(attributes, excludedAttributes, "displayName", groupTO.getName()));
1002
1003 SCIMConf conf = confManager.get();
1004
1005 Map<String, Attr> attrs = new HashMap<>();
1006 attrs.putAll(EntityTOUtils.buildAttrMap(groupTO.getPlainAttrs()));
1007 attrs.putAll(EntityTOUtils.buildAttrMap(groupTO.getDerAttrs()));
1008 attrs.putAll(EntityTOUtils.buildAttrMap(groupTO.getVirAttrs()));
1009
1010 if (output(attributes, excludedAttributes, "externalId")
1011 && conf.getGroupConf() != null
1012 && conf.getGroupConf().getExternalId() != null
1013 && attrs.containsKey(conf.getGroupConf().getExternalId())) {
1014
1015 group.setExternalId(attrs.get(conf.getGroupConf().getExternalId()).getValues().get(0));
1016 }
1017
1018 MembershipCond membCond = new MembershipCond();
1019 membCond.setGroup(groupTO.getKey());
1020 SearchCond searchCond = SearchCond.getLeaf(membCond);
1021
1022 if (output(attributes, excludedAttributes, "members")) {
1023 int count = userLogic.search(searchCond,
1024 1, 1, List.of(), SyncopeConstants.ROOT_REALM, true, false).getLeft();
1025
1026 for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
1027 List<UserTO> users = userLogic.search(
1028 searchCond,
1029 page,
1030 AnyDAO.DEFAULT_PAGE_SIZE,
1031 List.of(),
1032 SyncopeConstants.ROOT_REALM,
1033 true,
1034 false).
1035 getRight();
1036 users.forEach(userTO -> group.getMembers().add(new Member(
1037 userTO.getKey(),
1038 StringUtils.substringBefore(location, "/Groups") + "/Users/" + userTO.getKey(),
1039 userTO.getUsername())));
1040 }
1041 }
1042
1043 return group;
1044 }
1045
1046 public GroupTO toGroupTO(final SCIMGroup group, final boolean checkSchemas) {
1047 if (checkSchemas && !GROUP_SCHEMAS.equals(group.getSchemas())) {
1048 throw new BadRequestException(ErrorType.invalidValue);
1049 }
1050
1051 GroupTO groupTO = new GroupTO();
1052 groupTO.setRealm(SyncopeConstants.ROOT_REALM);
1053 groupTO.setKey(group.getId());
1054 groupTO.setName(group.getDisplayName());
1055
1056 SCIMConf conf = confManager.get();
1057 if (conf.getGroupConf() != null
1058 && conf.getGroupConf().getExternalId() != null && group.getExternalId() != null) {
1059
1060 groupTO.getPlainAttrs().add(
1061 new Attr.Builder(conf.getGroupConf().getExternalId()).
1062 value(group.getExternalId()).build());
1063 }
1064
1065 return groupTO;
1066 }
1067
1068 public GroupCR toGroupCR(final SCIMGroup group) {
1069 GroupTO groupTO = toGroupTO(group, true);
1070 GroupCR groupCR = new GroupCR();
1071 EntityTOUtils.toAnyCR(groupTO, groupCR);
1072 return groupCR;
1073 }
1074
1075 public GroupUR toGroupUR(final GroupTO before, final SCIMPatchOperation op) {
1076 if (op.getPath() == null) {
1077 throw new UnsupportedOperationException("Empty path not supported for Groups");
1078 }
1079
1080 GroupUR groupUR = new GroupUR.Builder(before.getKey()).build();
1081
1082 if ("displayName".equals(op.getPath().getAttribute())) {
1083 StringReplacePatchItem.Builder name = new StringReplacePatchItem.Builder().
1084 operation(op.getOp() == PatchOp.remove ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE);
1085 if (!CollectionUtils.isEmpty(op.getValue())) {
1086 name.value(op.getValue().get(0).toString());
1087 }
1088 groupUR.setName(name.build());
1089 } else {
1090 SCIMConf conf = confManager.get();
1091 if (conf.getGroupConf() != null) {
1092 setAttribute(groupUR.getPlainAttrs(), conf.getGroupConf().getExternalId(), op);
1093 }
1094 }
1095
1096 return groupUR;
1097 }
1098 }