1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.provisioning.java;
20
21 import java.util.Collection;
22 import java.util.HashSet;
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.lang3.tuple.Pair;
29 import org.apache.syncope.common.lib.request.PasswordPatch;
30 import org.apache.syncope.common.lib.request.StatusR;
31 import org.apache.syncope.common.lib.request.StringPatchItem;
32 import org.apache.syncope.common.lib.request.UserCR;
33 import org.apache.syncope.common.lib.request.UserUR;
34 import org.apache.syncope.common.lib.to.PropagationStatus;
35 import org.apache.syncope.common.lib.to.ProvisioningReport;
36 import org.apache.syncope.common.lib.types.AnyTypeKind;
37 import org.apache.syncope.common.lib.types.PatchOperation;
38 import org.apache.syncope.common.lib.types.ResourceOperation;
39 import org.apache.syncope.common.lib.types.StatusRType;
40 import org.apache.syncope.core.persistence.api.dao.UserDAO;
41 import org.apache.syncope.core.persistence.api.entity.user.User;
42 import org.apache.syncope.core.provisioning.api.PropagationByResource;
43 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
44 import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
45 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
46 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
47 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
48 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
49 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
50 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
51 import org.identityconnectors.framework.common.objects.Attribute;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.springframework.transaction.annotation.Propagation;
55 import org.springframework.transaction.annotation.Transactional;
56
57 public class DefaultUserProvisioningManager implements UserProvisioningManager {
58
59 protected static final Logger LOG = LoggerFactory.getLogger(UserProvisioningManager.class);
60
61 protected final UserWorkflowAdapter uwfAdapter;
62
63 protected final PropagationManager propagationManager;
64
65 protected final PropagationTaskExecutor taskExecutor;
66
67 protected final UserDAO userDAO;
68
69 protected final VirAttrHandler virtAttrHandler;
70
71 public DefaultUserProvisioningManager(
72 final UserWorkflowAdapter uwfAdapter,
73 final PropagationManager propagationManager,
74 final PropagationTaskExecutor taskExecutor,
75 final UserDAO userDAO,
76 final VirAttrHandler virtAttrHandler) {
77
78 this.uwfAdapter = uwfAdapter;
79 this.propagationManager = propagationManager;
80 this.taskExecutor = taskExecutor;
81 this.userDAO = userDAO;
82 this.virtAttrHandler = virtAttrHandler;
83 }
84
85 @Override
86 public Pair<String, List<PropagationStatus>> create(
87 final UserCR userCR, final boolean nullPriorityAsync, final String creator, final String context) {
88
89 return create(userCR, false, null, Set.of(), nullPriorityAsync, creator, context);
90 }
91
92 @Transactional(propagation = Propagation.REQUIRES_NEW)
93 @Override
94 public Pair<String, List<PropagationStatus>> create(
95 final UserCR userCR,
96 final boolean disablePwdPolicyCheck,
97 final Boolean enabled,
98 final Set<String> excludedResources,
99 final boolean nullPriorityAsync,
100 final String creator,
101 final String context) {
102
103 UserWorkflowResult<Pair<String, Boolean>> created =
104 uwfAdapter.create(userCR, disablePwdPolicyCheck, enabled, creator, context);
105
106 List<PropagationTaskInfo> taskInfos = propagationManager.getUserCreateTasks(
107 created.getResult().getLeft(),
108 userCR.getPassword(),
109 created.getResult().getRight(),
110 created.getPropByRes(),
111 created.getPropByLinkedAccount(),
112 userCR.getVirAttrs(),
113 excludedResources);
114 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, creator);
115
116 return Pair.of(created.getResult().getLeft(), propagationReporter.getStatuses());
117 }
118
119 @Override
120 public Pair<UserUR, List<PropagationStatus>> update(
121 final UserUR userUR, final boolean nullPriorityAsync, final String updater, final String context) {
122
123 Map<Pair<String, String>, Set<Attribute>> beforeAttrs = propagationManager.prepareAttrs(
124 AnyTypeKind.USER,
125 userUR.getKey(),
126 Optional.ofNullable(userUR.getPassword()).map(PasswordPatch::getValue).orElse(null),
127 userUR.getPassword() != null,
128 null,
129 Set.of());
130
131 UserWorkflowResult<Pair<UserUR, Boolean>> updated = uwfAdapter.update(userUR, updater, context);
132
133 List<PropagationTaskInfo> taskInfos = propagationManager.setAttributeDeltas(
134 propagationManager.getUserUpdateTasks(updated),
135 beforeAttrs,
136 updated.getResult().getLeft());
137 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, updater);
138
139 return Pair.of(updated.getResult().getLeft(), propagationReporter.getStatuses());
140 }
141
142 @Override
143 public Pair<UserUR, List<PropagationStatus>> update(
144 final UserUR userUR,
145 final Set<String> excludedResources,
146 final boolean nullPriorityAsync,
147 final String updater,
148 final String context) {
149
150 return update(userUR, new ProvisioningReport(), null, excludedResources, nullPriorityAsync, updater, context);
151 }
152
153 @Transactional(propagation = Propagation.REQUIRES_NEW)
154 @Override
155 public Pair<UserUR, List<PropagationStatus>> update(
156 final UserUR userUR,
157 final ProvisioningReport result,
158 final Boolean enabled,
159 final Set<String> excludedResources,
160 final boolean nullPriorityAsync,
161 final String updater,
162 final String context) {
163
164 Map<Pair<String, String>, Set<Attribute>> beforeAttrs = propagationManager.prepareAttrs(
165 AnyTypeKind.USER,
166 userUR.getKey(),
167 Optional.ofNullable(userUR.getPassword()).map(PasswordPatch::getValue).orElse(null),
168 userUR.getPassword() != null,
169 enabled,
170 excludedResources);
171
172 UserWorkflowResult<Pair<UserUR, Boolean>> updated;
173 try {
174 updated = uwfAdapter.update(userUR, updater, context);
175 } catch (Exception e) {
176 LOG.error("Update of user {} failed, trying to pull its status anyway (if configured)",
177 userUR.getKey(), e);
178
179 result.setStatus(ProvisioningReport.Status.FAILURE);
180 result.setMessage("Update failed, trying to pull status anyway (if configured)\n" + e.getMessage());
181
182 updated = new UserWorkflowResult<>(
183 Pair.of(userUR, false),
184 new PropagationByResource<>(),
185 new PropagationByResource<>(),
186 new HashSet<>());
187 }
188
189 if (enabled != null) {
190 User user = userDAO.find(userUR.getKey());
191
192 UserWorkflowResult<String> enableUpdate = null;
193 if (user.isSuspended() == null) {
194 enableUpdate = uwfAdapter.activate(userUR.getKey(), null, updater, context);
195 } else if (enabled && user.isSuspended()) {
196 enableUpdate = uwfAdapter.reactivate(userUR.getKey(), updater, context);
197 } else if (!enabled && !user.isSuspended()) {
198 enableUpdate = uwfAdapter.suspend(userUR.getKey(), updater, context);
199 }
200
201 if (enableUpdate != null) {
202 if (enableUpdate.getPropByRes() != null) {
203 updated.getPropByRes().merge(enableUpdate.getPropByRes());
204 updated.getPropByRes().purge();
205 }
206 updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks());
207 }
208 }
209
210 List<PropagationTaskInfo> taskInfos = propagationManager.setAttributeDeltas(
211 propagationManager.getUserUpdateTasks(
212 updated,
213 updated.getResult().getLeft().getPassword() != null,
214 excludedResources),
215 beforeAttrs,
216 updated.getResult().getLeft());
217 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, updater);
218
219 return Pair.of(updated.getResult().getLeft(), propagationReporter.getStatuses());
220 }
221
222 @Override
223 public List<PropagationStatus> delete(
224 final String key, final boolean nullPriorityAsync, final String eraser, final String context) {
225
226 return delete(key, Set.of(), nullPriorityAsync, eraser, context);
227 }
228
229 @Transactional(propagation = Propagation.REQUIRES_NEW)
230 @Override
231 public List<PropagationStatus> delete(
232 final String key,
233 final Set<String> excludedResources,
234 final boolean nullPriorityAsync,
235 final String eraser,
236 final String context) {
237
238 PropagationByResource<String> propByRes = new PropagationByResource<>();
239 propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceKeys(key));
240
241 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
242 userDAO.findLinkedAccounts(key).forEach(account -> propByLinkedAccount.add(
243 ResourceOperation.DELETE,
244 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
245
246
247
248
249
250
251 List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
252 AnyTypeKind.USER,
253 key,
254 propByRes,
255 propByLinkedAccount,
256 excludedResources);
257 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, eraser);
258
259 uwfAdapter.delete(key, eraser, context);
260
261 return propagationReporter.getStatuses();
262 }
263
264 @Override
265 public String unlink(final UserUR userUR, final String updater, final String context) {
266 UserWorkflowResult<Pair<UserUR, Boolean>> updated = uwfAdapter.update(userUR, updater, context);
267 return updated.getResult().getLeft().getKey();
268 }
269
270 @Override
271 public String link(final UserUR userUR, final String updater, final String context) {
272 return uwfAdapter.update(userUR, updater, context).getResult().getLeft().getKey();
273 }
274
275 @Override
276 public Pair<String, List<PropagationStatus>> activate(
277 final StatusR statusR, final boolean nullPriorityAsync, final String updater, final String context) {
278
279 UserWorkflowResult<String> updated = statusR.isOnSyncope()
280 ? uwfAdapter.activate(statusR.getKey(), statusR.getToken(), updater, context)
281 : new UserWorkflowResult<>(statusR.getKey(), null, null, statusR.getType().name().toLowerCase());
282
283 return Pair.of(updated.getResult(), propagateStatus(statusR, nullPriorityAsync, updater));
284 }
285
286 @Override
287 public Pair<String, List<PropagationStatus>> reactivate(
288 final StatusR statusR, final boolean nullPriorityAsync, final String updater, final String context) {
289
290 UserWorkflowResult<String> updated = statusR.isOnSyncope()
291 ? uwfAdapter.reactivate(statusR.getKey(), updater, context)
292 : new UserWorkflowResult<>(statusR.getKey(), null, null, statusR.getType().name().toLowerCase());
293
294 return Pair.of(updated.getResult(), propagateStatus(statusR, nullPriorityAsync, updater));
295 }
296
297 @Override
298 public Pair<String, List<PropagationStatus>> suspend(
299 final StatusR statusR, final boolean nullPriorityAsync, final String updater, final String context) {
300
301 UserWorkflowResult<String> updated = statusR.isOnSyncope()
302 ? uwfAdapter.suspend(statusR.getKey(), updater, context)
303 : new UserWorkflowResult<>(statusR.getKey(), null, null, statusR.getType().name().toLowerCase());
304
305 return Pair.of(updated.getResult(), propagateStatus(statusR, nullPriorityAsync, updater));
306 }
307
308 protected List<PropagationStatus> propagateStatus(
309 final StatusR statusR, final boolean nullPriorityAsync, final String updater) {
310
311 PropagationByResource<String> propByRes = new PropagationByResource<>();
312 propByRes.addAll(ResourceOperation.UPDATE, statusR.getResources());
313 List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
314 AnyTypeKind.USER,
315 statusR.getKey(),
316 false,
317 statusR.getType() != StatusRType.SUSPEND,
318 propByRes,
319 null,
320 null,
321 null);
322 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, updater);
323
324 return propagationReporter.getStatuses();
325 }
326
327 @Override
328 public void internalSuspend(final String key, final String updater, final String context) {
329 Pair<UserWorkflowResult<String>, Boolean> updated = uwfAdapter.internalSuspend(key, updater, context);
330
331
332 if (updated != null && updated.getRight()) {
333 UserUR userUR = new UserUR();
334 userUR.setKey(updated.getLeft().getResult());
335
336 List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(new UserWorkflowResult<>(
337 Pair.of(userUR, Boolean.FALSE),
338 updated.getLeft().getPropByRes(),
339 updated.getLeft().getPropByLinkedAccount(),
340 updated.getLeft().getPerformedTasks()));
341 taskExecutor.execute(taskInfos, false, updater);
342 }
343 }
344
345 @Override
346 public List<PropagationStatus> provision(
347 final String key,
348 final boolean changePwd,
349 final String password,
350 final Collection<String> resources,
351 final boolean nullPriorityAsync,
352 final String executor) {
353
354 UserUR userUR = new UserUR();
355 userUR.setKey(key);
356 userUR.getResources().addAll(resources.stream().
357 map(r -> new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(r).build()).
358 collect(Collectors.toSet()));
359
360 if (changePwd) {
361 PasswordPatch passwordPatch = new PasswordPatch();
362 passwordPatch.setOnSyncope(false);
363 passwordPatch.getResources().addAll(resources);
364 passwordPatch.setValue(password);
365 userUR.setPassword(passwordPatch);
366 }
367
368 PropagationByResource<String> propByRes = new PropagationByResource<>();
369 propByRes.addAll(ResourceOperation.UPDATE, resources);
370
371 UserWorkflowResult<Pair<UserUR, Boolean>> wfResult = new UserWorkflowResult<>(
372 Pair.of(userUR, (Boolean) null), propByRes, null, "update");
373
374 List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
375 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, executor);
376
377 return propagationReporter.getStatuses();
378 }
379
380 @Override
381 public List<PropagationStatus> deprovision(
382 final String key,
383 final Collection<String> resources,
384 final boolean nullPriorityAsync,
385 final String executor) {
386
387 PropagationByResource<String> propByRes = new PropagationByResource<>();
388 propByRes.set(ResourceOperation.DELETE, resources);
389
390 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
391 userDAO.findLinkedAccounts(key).stream().
392 filter(account -> resources.contains(account.getResource().getKey())).
393 forEach(account -> propByLinkedAccount.add(
394 ResourceOperation.DELETE,
395 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
396
397 List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
398 AnyTypeKind.USER,
399 key,
400 propByRes,
401 propByLinkedAccount,
402 userDAO.findAllResourceKeys(key).stream().
403 filter(resource -> !resources.contains(resource)).
404 collect(Collectors.toList()));
405 PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync, executor);
406
407 return propagationReporter.getStatuses();
408 }
409
410 @Override
411 public void requestPasswordReset(final String key, final String updater, final String context) {
412 uwfAdapter.requestPasswordReset(key, updater, context);
413 }
414
415 @Override
416 public void confirmPasswordReset(
417 final String key, final String token, final String password, final String updater, final String context) {
418
419 UserWorkflowResult<Pair<UserUR, Boolean>> updated =
420 uwfAdapter.confirmPasswordReset(key, token, password, updater, context);
421
422 List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(updated);
423 taskExecutor.execute(taskInfos, false, updater);
424 }
425 }