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.lang.reflect.Method;
22 import java.util.Collection;
23 import java.util.Comparator;
24 import java.util.List;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 import org.apache.commons.lang3.ArrayUtils;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.commons.lang3.tuple.Pair;
32 import org.apache.commons.lang3.tuple.Triple;
33 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
34 import org.apache.syncope.common.lib.SyncopeClientException;
35 import org.apache.syncope.common.lib.request.BooleanReplacePatchItem;
36 import org.apache.syncope.common.lib.request.MembershipUR;
37 import org.apache.syncope.common.lib.request.PasswordPatch;
38 import org.apache.syncope.common.lib.request.StatusR;
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.MembershipTO;
43 import org.apache.syncope.common.lib.to.PropagationStatus;
44 import org.apache.syncope.common.lib.to.ProvisioningResult;
45 import org.apache.syncope.common.lib.to.UserTO;
46 import org.apache.syncope.common.lib.types.AnyTypeKind;
47 import org.apache.syncope.common.lib.types.ClientExceptionType;
48 import org.apache.syncope.common.lib.types.EntityViolationType;
49 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
50 import org.apache.syncope.common.lib.types.PatchOperation;
51 import org.apache.syncope.common.rest.api.beans.ComplianceQuery;
52 import org.apache.syncope.core.logic.api.LogicActions;
53 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
54 import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
55 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
56 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
57 import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
58 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
59 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
60 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
61 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
62 import org.apache.syncope.core.persistence.api.dao.UserDAO;
63 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
64 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
65 import org.apache.syncope.core.persistence.api.entity.AccessToken;
66 import org.apache.syncope.core.persistence.api.entity.Entity;
67 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
68 import org.apache.syncope.core.persistence.api.entity.Realm;
69 import org.apache.syncope.core.persistence.api.entity.group.Group;
70 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
71 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
72 import org.apache.syncope.core.persistence.api.entity.user.User;
73 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
74 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
75 import org.apache.syncope.core.provisioning.api.rules.RuleEnforcer;
76 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
77 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
78 import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
79 import org.apache.syncope.core.spring.policy.AccountPolicyException;
80 import org.apache.syncope.core.spring.policy.PasswordPolicyException;
81 import org.apache.syncope.core.spring.security.AuthContextUtils;
82 import org.apache.syncope.core.spring.security.Encryptor;
83 import org.springframework.security.access.prepost.PreAuthorize;
84 import org.springframework.transaction.annotation.Transactional;
85
86
87
88
89
90 public class UserLogic extends AbstractAnyLogic<UserTO, UserCR, UserUR> {
91
92 protected final UserDAO userDAO;
93
94 protected final GroupDAO groupDAO;
95
96 protected final AnySearchDAO searchDAO;
97
98 protected final ExternalResourceDAO resourceDAO;
99
100 protected final AccessTokenDAO accessTokenDAO;
101
102 protected final DelegationDAO delegationDAO;
103
104 protected final ConfParamOps confParamOps;
105
106 protected final UserDataBinder binder;
107
108 protected final UserProvisioningManager provisioningManager;
109
110 protected final SyncopeLogic syncopeLogic;
111
112 protected final RuleEnforcer ruleEnforcer;
113
114 public UserLogic(
115 final RealmDAO realmDAO,
116 final AnyTypeDAO anyTypeDAO,
117 final TemplateUtils templateUtils,
118 final UserDAO userDAO,
119 final GroupDAO groupDAO,
120 final AnySearchDAO searchDAO,
121 final ExternalResourceDAO resourceDAO,
122 final AccessTokenDAO accessTokenDAO,
123 final DelegationDAO delegationDAO,
124 final ConfParamOps confParamOps,
125 final UserDataBinder binder,
126 final UserProvisioningManager provisioningManager,
127 final SyncopeLogic syncopeLogic,
128 final RuleEnforcer ruleEnforcer) {
129
130 super(realmDAO, anyTypeDAO, templateUtils);
131
132 this.userDAO = userDAO;
133 this.groupDAO = groupDAO;
134 this.searchDAO = searchDAO;
135 this.resourceDAO = resourceDAO;
136 this.accessTokenDAO = accessTokenDAO;
137 this.delegationDAO = delegationDAO;
138 this.confParamOps = confParamOps;
139 this.binder = binder;
140 this.provisioningManager = provisioningManager;
141 this.syncopeLogic = syncopeLogic;
142 this.ruleEnforcer = ruleEnforcer;
143 }
144
145 @PreAuthorize("isAuthenticated() and not(hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "'))")
146 @Transactional(readOnly = true)
147 public Triple<String, String, UserTO> selfRead() {
148 UserTO authenticatedUser = binder.getAuthenticatedUserTO();
149
150 return Triple.of(
151 POJOHelper.serialize(AuthContextUtils.getAuthorizations()),
152 POJOHelper.serialize(delegationDAO.findValidDelegating(authenticatedUser.getKey())), authenticatedUser);
153 }
154
155 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_READ + "')")
156 @Transactional(readOnly = true)
157 @Override
158 public UserTO read(final String key) {
159 return binder.getUserTO(key);
160 }
161
162 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_SEARCH + "')")
163 @Transactional(readOnly = true)
164 @Override
165 public Pair<Integer, List<UserTO>> search(
166 final SearchCond searchCond,
167 final int page, final int size, final List<OrderByClause> orderBy,
168 final String realm,
169 final boolean recursive,
170 final boolean details) {
171
172 Realm base = Optional.ofNullable(realmDAO.findByFullPath(realm)).
173 orElseThrow(() -> new NotFoundException("Realm " + realm));
174
175 Set<String> authRealms = RealmUtils.getEffective(
176 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_SEARCH), realm);
177
178 SearchCond effectiveCond = searchCond == null ? userDAO.getAllMatchingCond() : searchCond;
179
180 int count = searchDAO.count(base, recursive, authRealms, effectiveCond, AnyTypeKind.USER);
181
182 List<User> matching = searchDAO.search(
183 base, recursive, authRealms, effectiveCond, page, size, orderBy, AnyTypeKind.USER);
184 List<UserTO> result = matching.stream().
185 map(user -> binder.getUserTO(user, details)).
186 collect(Collectors.toList());
187
188 return Pair.of(count, result);
189 }
190
191 @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
192 public ProvisioningResult<UserTO> selfCreate(final UserCR createReq, final boolean nullPriorityAsync) {
193 return doCreate(createReq, true, nullPriorityAsync);
194 }
195
196 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_CREATE + "')")
197 public ProvisioningResult<UserTO> create(final UserCR createReq, final boolean nullPriorityAsync) {
198 return doCreate(createReq, false, nullPriorityAsync);
199 }
200
201 protected ProvisioningResult<UserTO> doCreate(
202 final UserCR userCR,
203 final boolean self,
204 final boolean nullPriorityAsync) {
205
206 Pair<UserCR, List<LogicActions>> before = beforeCreate(userCR);
207
208 if (before.getLeft().getRealm() == null) {
209 throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
210 }
211
212 if (!self) {
213 Set<String> authRealms = RealmUtils.getEffective(
214 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_CREATE),
215 before.getLeft().getRealm());
216 userDAO.securityChecks(
217 authRealms,
218 null,
219 before.getLeft().getRealm(),
220 before.getLeft().getMemberships().stream().filter(Objects::nonNull).
221 map(MembershipTO::getGroupKey).filter(Objects::nonNull).
222 collect(Collectors.toSet()));
223 }
224
225 Pair<String, List<PropagationStatus>> created = provisioningManager.create(
226 before.getLeft(), nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
227
228 return afterCreate(binder.getUserTO(created.getKey()), created.getRight(), before.getRight());
229 }
230
231 @PreAuthorize("isAuthenticated() "
232 + "and not(hasRole('" + IdRepoEntitlement.ANONYMOUS + "')) "
233 + "and not(hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "'))")
234 public ProvisioningResult<UserTO> selfUpdate(final UserUR userUR, final boolean nullPriorityAsync) {
235 UserTO userTO = binder.getAuthenticatedUserTO();
236 userUR.setKey(userTO.getKey());
237 ProvisioningResult<UserTO> updated = doUpdate(userUR, true, nullPriorityAsync);
238
239
240
241 List<String> authStatuses = List.of(confParamOps.get(AuthContextUtils.getDomain(),
242 "authentication.statuses", new String[] {}, String[].class));
243 if (!authStatuses.contains(updated.getEntity().getStatus())) {
244 Optional.ofNullable(accessTokenDAO.findByOwner(updated.getEntity().getUsername())).
245 map(AccessToken::getKey).ifPresent(accessTokenDAO::delete);
246 }
247
248 return updated;
249 }
250
251 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
252 @Override
253 public ProvisioningResult<UserTO> update(final UserUR userUR, final boolean nullPriorityAsync) {
254 return doUpdate(userUR, false, nullPriorityAsync);
255 }
256
257 protected Set<String> groups(final UserTO userTO) {
258 return userTO.getMemberships().stream().filter(Objects::nonNull).
259 map(MembershipTO::getGroupKey).filter(Objects::nonNull).
260 collect(Collectors.toSet());
261 }
262
263 protected ProvisioningResult<UserTO> doUpdate(
264 final UserUR userUR, final boolean self, final boolean nullPriorityAsync) {
265
266 UserTO userTO = binder.getUserTO(userUR.getKey());
267 Pair<UserUR, List<LogicActions>> before = beforeUpdate(userUR, userTO.getRealm());
268
269 Set<String> authRealms = RealmUtils.getEffective(
270 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_UPDATE),
271 userTO.getRealm());
272 if (!self) {
273 Set<String> groups = groups(userTO);
274 groups.removeAll(userUR.getMemberships().stream().filter(Objects::nonNull).
275 filter(m -> m.getOperation() == PatchOperation.DELETE).
276 map(MembershipUR::getGroup).filter(Objects::nonNull).
277 collect(Collectors.toSet()));
278
279 userDAO.securityChecks(
280 authRealms,
281 before.getLeft().getKey(),
282 userTO.getRealm(),
283 groups);
284 }
285
286 Pair<UserUR, List<PropagationStatus>> after = provisioningManager.update(
287 before.getLeft(), nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
288
289 ProvisioningResult<UserTO> result = afterUpdate(
290 binder.getUserTO(after.getLeft().getKey()),
291 after.getRight(),
292 before.getRight());
293
294 return result;
295 }
296
297 protected Pair<String, List<PropagationStatus>> setStatusOnWfAdapter(
298 final StatusR statusR, final boolean nullPriorityAsync) {
299
300 Pair<String, List<PropagationStatus>> updated;
301
302 switch (statusR.getType()) {
303 case SUSPEND:
304 updated = provisioningManager.suspend(
305 statusR, nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
306 break;
307
308 case REACTIVATE:
309 updated = provisioningManager.reactivate(
310 statusR, nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
311 break;
312
313 case ACTIVATE:
314 default:
315 updated = provisioningManager.activate(
316 statusR, nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
317 break;
318
319 }
320
321 return updated;
322 }
323
324 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
325 public ProvisioningResult<UserTO> status(final StatusR statusR, final boolean nullPriorityAsync) {
326
327 UserTO toUpdate = binder.getUserTO(statusR.getKey());
328
329 Set<String> authRealms = RealmUtils.getEffective(
330 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_UPDATE),
331 toUpdate.getRealm());
332 userDAO.securityChecks(
333 authRealms,
334 toUpdate.getKey(),
335 toUpdate.getRealm(),
336 groups(toUpdate));
337
338
339
340 statusR.setKey(toUpdate.getKey());
341 Pair<String, List<PropagationStatus>> updated = setStatusOnWfAdapter(statusR, nullPriorityAsync);
342
343 return afterUpdate(
344 binder.getUserTO(updated.getKey()),
345 updated.getRight(),
346 List.of());
347 }
348
349 @PreAuthorize("isAuthenticated() and not(hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "'))")
350 public ProvisioningResult<UserTO> selfStatus(final StatusR statusR, final boolean nullPriorityAsync) {
351 statusR.setKey(userDAO.findKey(AuthContextUtils.getUsername()));
352 Pair<String, List<PropagationStatus>> updated = setStatusOnWfAdapter(statusR, nullPriorityAsync);
353
354 return afterUpdate(
355 binder.getUserTO(updated.getKey()),
356 updated.getRight(),
357 List.of());
358 }
359
360 @PreAuthorize("hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "')")
361 public ProvisioningResult<UserTO> mustChangePassword(
362 final PasswordPatch password, final boolean nullPriorityAsync) {
363
364 UserTO userTO = binder.getAuthenticatedUserTO();
365
366 password.setOnSyncope(true);
367 password.getResources().clear();
368 password.getResources().addAll(userDAO.findAllResourceKeys(userTO.getKey()));
369
370 UserUR userUR = new UserUR.Builder(userTO.getKey()).
371 password(password).
372 mustChangePassword(new BooleanReplacePatchItem.Builder().value(false).build()).
373 build();
374 ProvisioningResult<UserTO> result = selfUpdate(userUR, nullPriorityAsync);
375
376 Optional.ofNullable(accessTokenDAO.findByOwner(result.getEntity().getUsername())).
377 map(AccessToken::getKey).ifPresent(accessTokenDAO::delete);
378
379 return result;
380 }
381
382 @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
383 @Transactional(readOnly = true)
384 public void compliance(final ComplianceQuery query) {
385 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RESTValidation);
386
387 if (query.isEmpty()) {
388 sce.getElements().add("Nothing to check");
389 throw sce;
390 }
391
392 Realm realm = null;
393 if (StringUtils.isNotBlank(query.getRealm())) {
394 realm = Optional.ofNullable(realmDAO.findByFullPath(query.getRealm())).
395 orElseThrow(() -> new NotFoundException("Realm " + query.getRealm()));
396 }
397 Set<ExternalResource> resources = query.getResources().stream().
398 map(resourceDAO::find).filter(Objects::nonNull).collect(Collectors.toSet());
399 if (realm == null && resources.isEmpty()) {
400 sce.getElements().add("Nothing to check");
401 throw sce;
402 }
403
404 if (StringUtils.isNotBlank(query.getUsername())) {
405 List<AccountPolicy> accountPolicies = ruleEnforcer.getAccountPolicies(realm, resources);
406 try {
407 if (accountPolicies.isEmpty()) {
408 if (!Entity.ID_PATTERN.matcher(query.getUsername()).matches()) {
409 throw new AccountPolicyException("Character(s) not allowed: " + query.getUsername());
410 }
411 } else {
412 for (AccountPolicy policy : accountPolicies) {
413 ruleEnforcer.getAccountRules(policy).forEach(rule -> rule.enforce(query.getUsername()));
414 }
415 }
416 } catch (AccountPolicyException e) {
417 throw new InvalidEntityException(User.class, EntityViolationType.InvalidUsername, e.getMessage());
418 }
419 }
420
421 if (StringUtils.isNotBlank(query.getPassword())) {
422 try {
423 for (PasswordPolicy policy : ruleEnforcer.getPasswordPolicies(realm, resources)) {
424 ruleEnforcer.getPasswordRules(policy).
425 forEach(rule -> rule.enforce(query.getUsername(), query.getPassword()));
426 }
427 } catch (PasswordPolicyException e) {
428 throw new InvalidEntityException(User.class, EntityViolationType.InvalidPassword, e.getMessage());
429 }
430 }
431 }
432
433 @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
434 @Transactional
435 public void requestPasswordReset(final String username, final String securityAnswer) {
436 User user = Optional.ofNullable(userDAO.findByUsername(username)).
437 orElseThrow(() -> new NotFoundException("User " + username));
438
439 if (syncopeLogic.isPwdResetRequiringSecurityQuestions()
440 && (securityAnswer == null || !Encryptor.getInstance().
441 verify(securityAnswer, user.getCipherAlgorithm(), user.getSecurityAnswer()))) {
442
443 throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer);
444 }
445
446 provisioningManager.requestPasswordReset(user.getKey(), AuthContextUtils.getUsername(), REST_CONTEXT);
447 }
448
449 @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
450 @Transactional
451 public void confirmPasswordReset(final String token, final String password) {
452 User user = Optional.ofNullable(userDAO.findByToken(token)).
453 orElseThrow(() -> new NotFoundException("User with token " + token));
454
455 provisioningManager.confirmPasswordReset(
456 user.getKey(), token, password, AuthContextUtils.getUsername(), REST_CONTEXT);
457 }
458
459 @PreAuthorize("isAuthenticated() "
460 + "and not(hasRole('" + IdRepoEntitlement.ANONYMOUS + "')) "
461 + "and not(hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "'))")
462 public ProvisioningResult<UserTO> selfDelete(final boolean nullPriorityAsync) {
463 return doDelete(binder.getAuthenticatedUserTO(), true, nullPriorityAsync);
464 }
465
466 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_DELETE + "')")
467 @Override
468 public ProvisioningResult<UserTO> delete(final String key, final boolean nullPriorityAsync) {
469 return doDelete(binder.getUserTO(key), false, nullPriorityAsync);
470 }
471
472 protected ProvisioningResult<UserTO> doDelete(
473 final UserTO userTO, final boolean self, final boolean nullPriorityAsync) {
474
475 Pair<UserTO, List<LogicActions>> before = beforeDelete(userTO);
476
477 if (!self) {
478 Set<String> authRealms = RealmUtils.getEffective(
479 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_DELETE),
480 before.getLeft().getRealm());
481 userDAO.securityChecks(
482 authRealms,
483 before.getLeft().getKey(),
484 before.getLeft().getRealm(),
485 groups(before.getLeft()));
486 }
487
488 List<Group> ownedGroups = groupDAO.findOwnedByUser(before.getLeft().getKey());
489 if (!ownedGroups.isEmpty()) {
490 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
491 sce.getElements().addAll(ownedGroups.stream().
492 map(group -> group.getKey() + ' ' + group.getName()).collect(Collectors.toList()));
493 throw sce;
494 }
495
496 List<PropagationStatus> statuses = provisioningManager.delete(
497 before.getLeft().getKey(), nullPriorityAsync, AuthContextUtils.getUsername(), REST_CONTEXT);
498
499 UserTO deletedTO;
500 if (userDAO.find(before.getLeft().getKey()) == null) {
501 deletedTO = new UserTO();
502 deletedTO.setKey(before.getLeft().getKey());
503 } else {
504 deletedTO = binder.getUserTO(before.getLeft().getKey());
505 }
506
507 return afterDelete(deletedTO, statuses, before.getRight());
508 }
509
510 protected void updateChecks(final String key) {
511 UserTO userTO = binder.getUserTO(key);
512
513 Set<String> authRealms = RealmUtils.getEffective(
514 AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_UPDATE),
515 userTO.getRealm());
516 userDAO.securityChecks(
517 authRealms,
518 userTO.getKey(),
519 userTO.getRealm(),
520 userTO.getMemberships().stream().
521 map(MembershipTO::getGroupKey).
522 collect(Collectors.toSet()));
523 }
524
525 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
526 @Override
527 public UserTO unlink(final String key, final Collection<String> resources) {
528 updateChecks(key);
529
530 UserUR req = new UserUR.Builder(key).
531 resources(resources.stream().
532 map(r -> new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(r).build()).
533 collect(Collectors.toList())).
534 build();
535
536 return binder.getUserTO(provisioningManager.unlink(req, AuthContextUtils.getUsername(), REST_CONTEXT));
537 }
538
539 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
540 @Override
541 public UserTO link(final String key, final Collection<String> resources) {
542 updateChecks(key);
543
544 UserUR req = new UserUR.Builder(key).
545 resources(resources.stream().
546 map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()).
547 collect(Collectors.toList())).
548 build();
549
550 return binder.getUserTO(provisioningManager.link(req, AuthContextUtils.getUsername(), REST_CONTEXT));
551 }
552
553 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
554 @Override
555 public ProvisioningResult<UserTO> unassign(
556 final String key, final Collection<String> resources, final boolean nullPriorityAsync) {
557
558 updateChecks(key);
559
560 UserUR req = new UserUR.Builder(key).
561 resources(resources.stream().
562 map(r -> new StringPatchItem.Builder().operation(PatchOperation.DELETE).value(r).build()).
563 collect(Collectors.toList())).
564 build();
565
566 return update(req, nullPriorityAsync);
567 }
568
569 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
570 @Override
571 public ProvisioningResult<UserTO> assign(
572 final String key,
573 final Collection<String> resources,
574 final boolean changepwd,
575 final String password,
576 final boolean nullPriorityAsync) {
577
578 updateChecks(key);
579
580 UserUR req = new UserUR.Builder(key).
581 resources(resources.stream().
582 map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()).
583 collect(Collectors.toList())).
584 build();
585
586 if (changepwd) {
587 req.setPassword(new PasswordPatch.Builder().
588 value(password).onSyncope(false).resources(resources).build());
589 }
590
591 return update(req, nullPriorityAsync);
592 }
593
594 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
595 @Override
596 public ProvisioningResult<UserTO> deprovision(
597 final String key,
598 final List<String> resources,
599 final boolean nullPriorityAsync) {
600
601 updateChecks(key);
602
603 List<PropagationStatus> statuses = provisioningManager.deprovision(
604 key, resources, nullPriorityAsync, AuthContextUtils.getUsername());
605
606 ProvisioningResult<UserTO> result = new ProvisioningResult<>();
607 result.setEntity(binder.getUserTO(key));
608 result.getPropagationStatuses().addAll(statuses);
609 result.getPropagationStatuses().sort(Comparator.comparing(item -> resources.indexOf(item.getResource())));
610 return result;
611 }
612
613 @PreAuthorize("hasRole('" + IdRepoEntitlement.USER_UPDATE + "')")
614 @Override
615 public ProvisioningResult<UserTO> provision(
616 final String key,
617 final List<String> resources,
618 final boolean changePwd,
619 final String password,
620 final boolean nullPriorityAsync) {
621
622 updateChecks(key);
623
624 List<PropagationStatus> statuses = provisioningManager.provision(
625 key, changePwd, password, resources, nullPriorityAsync, AuthContextUtils.getUsername());
626
627 ProvisioningResult<UserTO> result = new ProvisioningResult<>();
628 result.setEntity(binder.getUserTO(key));
629 result.getPropagationStatuses().addAll(statuses);
630 result.getPropagationStatuses().sort(Comparator.comparing(item -> resources.indexOf(item.getResource())));
631 return result;
632 }
633
634 @Override
635 protected UserTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
636 String key = null;
637
638 if ("requestPasswordReset".equals(method.getName())) {
639 key = userDAO.findKey((String) args[0]);
640 } else if (!"confirmPasswordReset".equals(method.getName()) && ArrayUtils.isNotEmpty(args)) {
641 for (int i = 0; key == null && i < args.length; i++) {
642 if (args[i] instanceof String) {
643 key = (String) args[i];
644 } else if (args[i] instanceof UserTO) {
645 key = ((UserTO) args[i]).getKey();
646 } else if (args[i] instanceof UserUR) {
647 key = ((UserUR) args[i]).getKey();
648 } else if (args[i] instanceof StatusR) {
649 key = ((StatusR) args[i]).getKey();
650 }
651 }
652 }
653
654 if (key != null) {
655 try {
656 return binder.getUserTO(key);
657 } catch (Throwable ignore) {
658 LOG.debug("Unresolved reference", ignore);
659 throw new UnresolvedReferenceException(ignore);
660 }
661 }
662
663 throw new UnresolvedReferenceException();
664 }
665 }