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.pushpull;
20
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Optional;
24 import java.util.stream.Collectors;
25 import org.apache.commons.lang3.BooleanUtils;
26 import org.apache.commons.lang3.exception.ExceptionUtils;
27 import org.apache.commons.lang3.tuple.Pair;
28 import org.apache.syncope.common.lib.request.AnyUR;
29 import org.apache.syncope.common.lib.request.UserUR;
30 import org.apache.syncope.common.lib.to.AnyTO;
31 import org.apache.syncope.common.lib.to.Provision;
32 import org.apache.syncope.common.lib.to.ProvisioningReport;
33 import org.apache.syncope.common.lib.types.AnyTypeKind;
34 import org.apache.syncope.common.lib.types.MatchType;
35 import org.apache.syncope.common.lib.types.MatchingRule;
36 import org.apache.syncope.common.lib.types.ResourceOperation;
37 import org.apache.syncope.common.lib.types.UnmatchingRule;
38 import org.apache.syncope.core.persistence.api.entity.Any;
39 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
40 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
41 import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
42 import org.apache.syncope.core.persistence.api.entity.user.User;
43 import org.apache.syncope.core.provisioning.api.PropagationByResource;
44 import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
45 import org.apache.syncope.core.provisioning.api.WorkflowResult;
46 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
47 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
48 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
49 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
50 import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
51 import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
52 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
53 import org.identityconnectors.framework.common.objects.ConnectorObject;
54 import org.quartz.JobExecutionException;
55 import org.springframework.transaction.annotation.Propagation;
56 import org.springframework.transaction.annotation.Transactional;
57
58 public class DefaultUserPushResultHandler extends AbstractPushResultHandler implements UserPushResultHandler {
59
60 @Override
61 protected AnyUtils getAnyUtils() {
62 return anyUtilsFactory.getInstance(AnyTypeKind.USER);
63 }
64
65 @Override
66 protected String getName(final Any<?> any) {
67 return User.class.cast(any).getUsername();
68 }
69
70 @Override
71 protected AnyTO getAnyTO(final Any<?> any) {
72 return userDataBinder.getUserTO((User) any, true);
73 }
74
75 @Override
76 protected void provision(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
77 AnyTO before = getAnyTO(any);
78
79 List<String> noPropResources = new ArrayList<>(before.getResources());
80 noPropResources.remove(profile.getTask().getResource().getKey());
81
82 PropagationByResource<String> propByRes = new PropagationByResource<>();
83 propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
84
85 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
86 ((User) any).getLinkedAccounts(profile.getTask().getResource().getKey()).
87 forEach(account -> propByLinkedAccount.add(
88 ResourceOperation.CREATE,
89 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
90
91 PropagationReporter reporter = taskExecutor.execute(propagationManager.getUserCreateTasks(
92 before.getKey(),
93 null,
94 enabled,
95 propByRes,
96 propByLinkedAccount,
97 before.getVirAttrs(),
98 noPropResources),
99 false,
100 profile.getExecutor());
101 reportPropagation(result, reporter);
102 }
103
104 @Override
105 protected void update(
106 final Any<?> any,
107 final Boolean enable,
108 final ConnectorObject beforeObj,
109 final ProvisioningReport result) {
110
111 List<String> ownedResources = getAnyUtils().getAllResources(any).stream().
112 map(ExternalResource::getKey).collect(Collectors.toList());
113
114 List<String> noPropResources = new ArrayList<>(ownedResources);
115 noPropResources.remove(profile.getTask().getResource().getKey());
116
117 PropagationByResource<String> propByRes = new PropagationByResource<>();
118 propByRes.add(ResourceOperation.UPDATE, profile.getTask().getResource().getKey());
119 propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
120
121 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
122 ((User) any).getLinkedAccounts(profile.getTask().getResource().getKey()).
123 forEach(account -> propByLinkedAccount.add(
124 ResourceOperation.UPDATE,
125 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
126
127 List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
128 any.getType().getKind(),
129 any.getKey(),
130 true,
131 enable,
132 propByRes,
133 propByLinkedAccount,
134 null,
135 noPropResources);
136 if (!taskInfos.isEmpty()) {
137 taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
138 PropagationReporter reporter = new DefaultPropagationReporter();
139 taskExecutor.execute(taskInfos.get(0), reporter, profile.getExecutor());
140 reportPropagation(result, reporter);
141 }
142 }
143
144 @Override
145 protected void deprovision(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
146 AnyTO before = getAnyTO(any);
147
148 List<String> noPropResources = new ArrayList<>(before.getResources());
149 noPropResources.remove(profile.getTask().getResource().getKey());
150
151 PropagationByResource<String> propByRes = new PropagationByResource<>();
152 propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
153 propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
154
155 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
156 ((User) any).getLinkedAccounts(profile.getTask().getResource().getKey()).
157 forEach(account -> propByLinkedAccount.add(
158 ResourceOperation.DELETE,
159 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
160
161 List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
162 any.getType().getKind(),
163 any.getKey(),
164 propByRes,
165 propByLinkedAccount,
166 noPropResources);
167 if (!taskInfos.isEmpty()) {
168 taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
169 PropagationReporter reporter = new DefaultPropagationReporter();
170 taskExecutor.execute(taskInfos.get(0), reporter, profile.getExecutor());
171 reportPropagation(result, reporter);
172 }
173 }
174
175 @Override
176 protected WorkflowResult<? extends AnyUR> update(final AnyUR req) {
177 WorkflowResult<Pair<UserUR, Boolean>> update =
178 uwfAdapter.update((UserUR) req, profile.getExecutor(), getContext());
179 return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks());
180 }
181
182 @Transactional(propagation = Propagation.REQUIRES_NEW)
183 @Override
184 public boolean handle(final LinkedAccount account, final Provision provision) {
185 try {
186 doHandle(account, provision);
187 return true;
188 } catch (IgnoreProvisionException e) {
189 ProvisioningReport ignoreResult = profile.getResults().stream().
190 filter(report -> account.getKey().equalsIgnoreCase(report.getKey())).
191 findFirst().
192 orElse(null);
193 if (ignoreResult == null) {
194 ignoreResult = new ProvisioningReport();
195 ignoreResult.setKey(account.getKey());
196 ignoreResult.setAnyType(MatchType.LINKED_ACCOUNT.name());
197 ignoreResult.setUidValue(account.getConnObjectKeyValue());
198
199 profile.getResults().add(ignoreResult);
200 }
201
202 ignoreResult.setOperation(ResourceOperation.NONE);
203 ignoreResult.setStatus(ProvisioningReport.Status.IGNORE);
204 ignoreResult.setMessage(e.getMessage());
205
206 LOG.warn("Ignoring during push", e);
207 return true;
208 } catch (JobExecutionException e) {
209 LOG.error("Push failed", e);
210 return false;
211 }
212 }
213
214 protected void doHandle(final LinkedAccount account, final Provision provision) throws JobExecutionException {
215 ProvisioningReport result = new ProvisioningReport();
216 profile.getResults().add(result);
217
218 result.setKey(account.getKey());
219 result.setAnyType(MatchType.LINKED_ACCOUNT.name());
220 result.setUidValue(account.getConnObjectKeyValue());
221 result.setName(account.getConnObjectKeyValue());
222
223 LOG.debug("Pushing linked account {} towards {}", account.getKey(), profile.getTask().getResource());
224
225
226 Optional<ConnectorObject> connObj = MappingUtils.getConnObjectKeyItem(provision).
227 flatMap(connObjectKeyItem -> outboundMatcher.matchByConnObjectKeyValue(
228 profile.getConnector(),
229 connObjectKeyItem,
230 account.getConnObjectKeyValue(),
231 profile.getTask().getResource(),
232 provision,
233 Optional.empty(),
234 Optional.empty()));
235 LOG.debug("Match found for linked account {} as {}: {}", account, provision.getObjectClass(), connObj);
236
237 ConnectorObject beforeObj = connObj.orElse(null);
238
239 if (profile.isDryRun()) {
240 if (beforeObj == null) {
241 result.setOperation(toResourceOperation(profile.getTask().getUnmatchingRule()));
242 } else {
243 result.setOperation(toResourceOperation(profile.getTask().getMatchingRule()));
244 }
245 result.setStatus(ProvisioningReport.Status.SUCCESS);
246 } else {
247 Boolean enable = profile.getTask().isSyncStatus()
248 ? BooleanUtils.negate(account.isSuspended())
249 : null;
250 try {
251 if (beforeObj == null) {
252 result.setOperation(toResourceOperation(profile.getTask().getUnmatchingRule()));
253
254 switch (profile.getTask().getUnmatchingRule()) {
255 case ASSIGN:
256 case PROVISION:
257 for (PushActions action : profile.getActions()) {
258 if (profile.getTask().getUnmatchingRule() == UnmatchingRule.ASSIGN) {
259 action.beforeAssign(profile, account);
260 } else {
261 action.beforeProvision(profile, account);
262 }
263 }
264
265 if (!profile.getTask().isPerformCreate()) {
266 LOG.debug("PushTask not configured for create");
267 result.setStatus(ProvisioningReport.Status.IGNORE);
268 } else {
269 provision(account, enable, result);
270 }
271 break;
272
273 case UNLINK:
274 LOG.warn("{} not applicable to linked accounts, ignoring",
275 profile.getTask().getUnmatchingRule());
276 break;
277
278 case IGNORE:
279 result.setStatus(ProvisioningReport.Status.IGNORE);
280 break;
281
282 default:
283
284 }
285 } else {
286 result.setOperation(toResourceOperation(profile.getTask().getMatchingRule()));
287
288 switch (profile.getTask().getMatchingRule()) {
289 case UPDATE:
290 for (PushActions action : profile.getActions()) {
291 action.beforeUpdate(profile, account);
292 }
293 if (!profile.getTask().isPerformUpdate()) {
294 LOG.debug("PushTask not configured for update");
295 result.setStatus(ProvisioningReport.Status.IGNORE);
296 } else {
297 update(account, enable, beforeObj, ResourceOperation.UPDATE, result);
298 }
299 break;
300
301 case UNASSIGN:
302 case DEPROVISION:
303 for (PushActions action : profile.getActions()) {
304 if (profile.getTask().getMatchingRule() == MatchingRule.UNASSIGN) {
305 action.beforeUnassign(profile, account);
306 } else {
307 action.beforeDeprovision(profile, account);
308 }
309 }
310
311 if (!profile.getTask().isPerformDelete()) {
312 LOG.debug("PushTask not configured for delete");
313 result.setStatus(ProvisioningReport.Status.IGNORE);
314 } else {
315 update(account, enable, beforeObj, ResourceOperation.DELETE, result);
316 }
317 break;
318
319 case LINK:
320 case UNLINK:
321 LOG.warn("{} not applicable to linked accounts, ignoring",
322 profile.getTask().getMatchingRule());
323 break;
324
325 case IGNORE:
326 result.setStatus(ProvisioningReport.Status.IGNORE);
327 break;
328
329 default:
330
331 }
332 }
333
334 for (PushActions action : profile.getActions()) {
335 action.after(profile, account, result);
336 }
337
338 if (result.getStatus() == null) {
339 result.setStatus(ProvisioningReport.Status.SUCCESS);
340 }
341 } catch (IgnoreProvisionException e) {
342 throw e;
343 } catch (Exception e) {
344 result.setStatus(ProvisioningReport.Status.FAILURE);
345 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
346
347 LOG.warn("Error pushing linked account {} towards {}", account, profile.getTask().getResource(), e);
348
349 for (PushActions action : profile.getActions()) {
350 action.onError(profile, account, result, e);
351 }
352
353 throw new JobExecutionException(e);
354 }
355 }
356 }
357
358 protected void provision(
359 final LinkedAccount account,
360 final Boolean enable,
361 final ProvisioningReport result) {
362
363 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
364 propByLinkedAccount.add(
365 ResourceOperation.CREATE,
366 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue()));
367
368 List<PropagationTaskInfo> taskInfos = propagationManager.getUserCreateTasks(
369 account.getOwner().getKey(),
370 null,
371 enable,
372 new PropagationByResource<>(),
373 propByLinkedAccount,
374 null,
375 null);
376 if (!taskInfos.isEmpty()) {
377 taskInfos.get(0).setBeforeObj(Optional.empty());
378 PropagationReporter reporter = new DefaultPropagationReporter();
379 taskExecutor.execute(taskInfos.get(0), reporter, profile.getExecutor());
380 reportPropagation(result, reporter);
381 }
382 }
383
384 protected void update(
385 final LinkedAccount account,
386 final Boolean enable,
387 final ConnectorObject beforeObj,
388 final ResourceOperation operation,
389 final ProvisioningReport result) {
390
391 UserUR req = new UserUR();
392 req.setKey(account.getOwner().getKey());
393
394 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
395 propByLinkedAccount.add(operation, Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue()));
396
397 List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
398 new UserWorkflowResult<>(
399 Pair.of(req, enable),
400 new PropagationByResource<>(),
401 propByLinkedAccount,
402 ""));
403 if (!taskInfos.isEmpty()) {
404 taskInfos.get(0).setBeforeObj(Optional.empty());
405 PropagationReporter reporter = new DefaultPropagationReporter();
406 taskExecutor.execute(taskInfos.get(0), reporter, profile.getExecutor());
407 reportPropagation(result, reporter);
408 }
409 }
410 }