1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.client.console.batch;
20
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import org.apache.cxf.helpers.CastUtils;
31 import org.apache.syncope.client.console.SyncopeConsoleSession;
32 import org.apache.syncope.client.console.pages.BasePage;
33 import org.apache.syncope.client.console.panels.MultilevelPanel;
34 import org.apache.syncope.client.console.rest.AbstractAnyRestClient;
35 import org.apache.syncope.client.console.rest.AnyObjectRestClient;
36 import org.apache.syncope.client.console.rest.GroupRestClient;
37 import org.apache.syncope.client.console.rest.UserRestClient;
38 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BatchResponseColumn;
39 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
40 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
41 import org.apache.syncope.client.lib.batch.BatchRequest;
42 import org.apache.syncope.client.ui.commons.Constants;
43 import org.apache.syncope.client.ui.commons.rest.RestClient;
44 import org.apache.syncope.client.ui.commons.status.StatusBean;
45 import org.apache.syncope.common.lib.request.BooleanReplacePatchItem;
46 import org.apache.syncope.common.lib.request.StatusR;
47 import org.apache.syncope.common.lib.request.UserUR;
48 import org.apache.syncope.common.lib.to.AnyTO;
49 import org.apache.syncope.common.lib.to.ExecTO;
50 import org.apache.syncope.common.lib.to.GroupTO;
51 import org.apache.syncope.common.lib.to.ReportTO;
52 import org.apache.syncope.common.lib.to.TaskTO;
53 import org.apache.syncope.common.lib.to.UserTO;
54 import org.apache.syncope.common.lib.types.ExecStatus;
55 import org.apache.syncope.common.lib.types.ResourceAssociationAction;
56 import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
57 import org.apache.syncope.common.lib.types.StatusRType;
58 import org.apache.syncope.common.lib.types.TaskType;
59 import org.apache.syncope.common.rest.api.beans.ExecSpecs;
60 import org.apache.syncope.common.rest.api.service.AnyObjectService;
61 import org.apache.syncope.common.rest.api.service.AnyService;
62 import org.apache.syncope.common.rest.api.service.GroupService;
63 import org.apache.syncope.common.rest.api.service.ReportService;
64 import org.apache.syncope.common.rest.api.service.TaskService;
65 import org.apache.syncope.common.rest.api.service.UserService;
66 import org.apache.wicket.PageReference;
67 import org.apache.wicket.ajax.AjaxRequestTarget;
68 import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
69 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
70 import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
71 import org.apache.wicket.markup.html.WebMarkupContainer;
72 import org.apache.wicket.model.CompoundPropertyModel;
73 import org.apache.wicket.model.IModel;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76 import org.springframework.util.CollectionUtils;
77
78 public class BatchContent<T extends Serializable, S> extends MultilevelPanel.SecondLevel {
79
80 private static final long serialVersionUID = 4114026480146090963L;
81
82 private static final Logger LOG = LoggerFactory.getLogger(BatchContent.class);
83
84 private WebMarkupContainer container;
85
86 private ActionsPanel<Serializable> actionPanel;
87
88 private SortableDataProvider<T, S> dataProvider;
89
90 public BatchContent(
91 final List<T> items,
92 final List<IColumn<T, S>> columns,
93 final Collection<ActionLink.ActionType> actions,
94 final RestClient batchExecutor,
95 final String keyFieldName) {
96
97 this(MultilevelPanel.SECOND_LEVEL_ID, items, columns, actions, batchExecutor, keyFieldName);
98 }
99
100 public BatchContent(
101 final String id,
102 final List<T> items,
103 final List<IColumn<T, S>> columns,
104 final Collection<ActionLink.ActionType> actions,
105 final RestClient batchExecutor,
106 final String keyFieldName) {
107
108 super(id);
109
110 setup(items, columns);
111
112 for (ActionLink.ActionType action : actions) {
113 actionPanel.add(new ActionLink<>() {
114
115 private static final long serialVersionUID = -3722207913631435501L;
116
117 @Override
118 protected boolean statusCondition(final Serializable modelObject) {
119 return !CollectionUtils.isEmpty(items);
120 }
121
122 @Override
123 @SuppressWarnings("unchecked")
124 public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
125 if (CollectionUtils.isEmpty(items)) {
126 throw new IllegalArgumentException("Invalid items");
127 }
128
129 Map<String, String> results;
130 try {
131 T singleItem = items.iterator().next();
132
133 if (singleItem instanceof ExecTO) {
134 results = new HashMap<>();
135 items.forEach(item -> {
136 ExecTO exec = ExecTO.class.cast(item);
137
138 try {
139 batchExecutor.getClass().getMethod("deleteExecution", String.class).
140 invoke(batchExecutor, exec.getKey());
141 results.put(exec.getKey(), ExecStatus.SUCCESS.name());
142 } catch (Exception e) {
143 LOG.error("Error deleting execution {}", exec.getKey(), e);
144 results.put(exec.getKey(), ExecStatus.FAILURE.name());
145 }
146 });
147 } else if (singleItem instanceof StatusBean) {
148 AbstractAnyRestClient<?> anyRestClient = AbstractAnyRestClient.class.cast(batchExecutor);
149
150
151 Map<String, List<StatusBean>> beans = new HashMap<>();
152 items.stream().map(StatusBean.class::cast).
153 forEachOrdered(sb -> {
154 final List<StatusBean> sblist;
155 if (beans.containsKey(sb.getKey())) {
156 sblist = beans.get(sb.getKey());
157 } else {
158 sblist = new ArrayList<>();
159 beans.put(sb.getKey(), sblist);
160 }
161 sblist.add(sb);
162 });
163
164 results = new HashMap<>();
165 beans.forEach((key, value) -> {
166 String etag = anyRestClient.read(key).getETagValue();
167
168 switch (action) {
169 case DEPROVISION:
170 results.putAll(anyRestClient.deassociate(
171 ResourceDeassociationAction.DEPROVISION, etag, key, value));
172 break;
173
174 case UNASSIGN:
175 results.putAll(anyRestClient.deassociate(
176 ResourceDeassociationAction.UNASSIGN, etag, key, value));
177 break;
178
179 case UNLINK:
180 results.putAll(anyRestClient.deassociate(
181 ResourceDeassociationAction.UNLINK, etag, key, value));
182 break;
183
184 case ASSIGN:
185 results.putAll(anyRestClient.associate(
186 ResourceAssociationAction.ASSIGN, etag, key, value));
187 break;
188
189 case LINK:
190 results.putAll(anyRestClient.associate(
191 ResourceAssociationAction.LINK, etag, key, value));
192 break;
193
194 case PROVISION:
195 results.putAll(anyRestClient.associate(
196 ResourceAssociationAction.PROVISION, etag, key, value));
197 break;
198
199 case SUSPEND:
200 results.putAll(((UserRestClient) anyRestClient).suspend(etag, key, value));
201 break;
202
203 case REACTIVATE:
204 results.putAll(((UserRestClient) anyRestClient).reactivate(etag, key, value));
205 break;
206
207 default:
208 }
209 });
210 } else {
211 BatchRequest batch = SyncopeConsoleSession.get().batch();
212
213 UserService batchUserService = batch.getService(UserService.class);
214 GroupService batchGroupService = batch.getService(GroupService.class);
215 AnyObjectService batchAnyObjectService = batch.getService(AnyObjectService.class);
216 AnyService<?> batchAnyService = singleItem instanceof UserTO
217 ? batchUserService
218 : singleItem instanceof GroupTO
219 ? batchGroupService
220 : batchAnyObjectService;
221 TaskService batchTaskService = batch.getService(TaskService.class);
222 ReportService batchReportService = batch.getService(ReportService.class);
223
224 Set<String> deletedAnys = new HashSet<>();
225
226 switch (action) {
227 case MUSTCHANGEPASSWORD:
228 items.forEach(item -> {
229 UserTO user = (UserTO) item;
230
231 UserUR req = new UserUR();
232 req.setKey(user.getKey());
233 req.setMustChangePassword(new BooleanReplacePatchItem.Builder().
234 value(!user.isMustChangePassword()).build());
235
236 batchUserService.update(req);
237 });
238 break;
239
240 case SUSPEND:
241 items.forEach(item -> {
242 UserTO user = (UserTO) item;
243
244 StatusR req = new StatusR.Builder(user.getKey(), StatusRType.SUSPEND).
245 onSyncope(true).
246 resources(user.getResources()).
247 build();
248
249 batchUserService.status(req);
250 });
251 break;
252
253 case REACTIVATE:
254 items.forEach(item -> {
255 UserTO user = (UserTO) item;
256
257 StatusR req = new StatusR.Builder(user.getKey(), StatusRType.REACTIVATE).
258 onSyncope(true).
259 resources(user.getResources()).
260 build();
261
262 batchUserService.status(req);
263 });
264 break;
265
266 case DELETE:
267 items.forEach(item -> {
268 if (singleItem instanceof AnyTO) {
269 AnyTO any = (AnyTO) item;
270
271 batchAnyService.delete(any.getKey());
272 deletedAnys.add(any.getKey());
273 } else if (singleItem instanceof TaskTO) {
274 TaskTO task = (TaskTO) item;
275
276 batchTaskService.delete(
277 TaskType.fromTOClass(task.getClass()),
278 task.getKey());
279 } else if (singleItem instanceof ReportTO) {
280 ReportTO report = (ReportTO) item;
281
282 batchReportService.delete(report.getKey());
283 } else {
284 LOG.warn("Unsupported for DELETE: {}", singleItem.getClass().getName());
285 }
286 });
287
288 break;
289
290 case DRYRUN:
291 items.forEach(item -> {
292 TaskTO task = (TaskTO) item;
293
294 batchTaskService.execute(
295 new ExecSpecs.Builder().dryRun(true).key(task.getKey()).build());
296 });
297 break;
298
299 case EXECUTE:
300 items.forEach(item -> {
301 if (singleItem instanceof TaskTO) {
302 TaskTO task = (TaskTO) item;
303
304 batchTaskService.execute(
305 new ExecSpecs.Builder().dryRun(false).key(task.getKey()).build());
306 } else if (singleItem instanceof ReportTO) {
307 ReportTO report = (ReportTO) item;
308
309 batchReportService.execute(
310 new ExecSpecs.Builder().key(report.getKey()).build());
311 }
312 });
313 break;
314
315 default:
316 }
317
318 results = CastUtils.cast(Map.class.cast(
319 batchExecutor.getClass().getMethod("batch",
320 BatchRequest.class).invoke(batchExecutor, batch)));
321
322 if (singleItem instanceof AnyTO) {
323 AbstractAnyRestClient<? extends AnyTO> anyRestClient = singleItem instanceof UserTO
324 ? UserRestClient.class.cast(batchExecutor)
325 : singleItem instanceof GroupTO
326 ? GroupRestClient.class.cast(batchExecutor)
327 : AnyObjectRestClient.class.cast(batchExecutor);
328 for (int i = 0; i < items.size(); i++) {
329 String key = ((AnyTO) items.get(i)).getKey();
330 if (!deletedAnys.contains(key)) {
331 items.set(i, (T) anyRestClient.read(key));
332 }
333 }
334 }
335 }
336
337 List<IColumn<T, S>> newColumnList = new ArrayList<>(columns);
338 newColumnList.add(newColumnList.size(), new BatchResponseColumn<>(results, keyFieldName));
339
340 container.addOrReplace(new AjaxFallbackDefaultDataTable<>(
341 "selectedObjects",
342 newColumnList,
343 dataProvider,
344 Integer.MAX_VALUE).setVisible(!items.isEmpty()));
345
346 actionPanel.setEnabled(false);
347 actionPanel.setVisible(false);
348 target.add(container);
349 target.add(actionPanel);
350
351 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
352 } catch (Exception e) {
353 LOG.error("Batch failure", e);
354 SyncopeConsoleSession.get().error("Operation " + action.getActionId() + " failed");
355 }
356 ((BasePage) getPage()).getNotificationPanel().refresh(target);
357 }
358 }, action, null).hideLabel();
359 }
360 }
361
362 public BatchContent(
363 final String id,
364 final List<T> items,
365 final List<IColumn<T, S>> columns,
366 final Map<String, String> results,
367 final String keyFieldName,
368 final AjaxRequestTarget target,
369 final PageReference pageRef) {
370
371 super(id);
372
373 List<IColumn<T, S>> newColumnList = new ArrayList<>(columns);
374 newColumnList.add(newColumnList.size(), new BatchResponseColumn<>(results, keyFieldName));
375 setup(items, newColumnList);
376
377 actionPanel.setEnabled(false);
378 actionPanel.setVisible(false);
379 target.add(container);
380 target.add(actionPanel);
381
382 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
383 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
384 }
385
386 private void setup(
387 final List<T> items,
388 final List<IColumn<T, S>> columns) {
389
390 container = new WebMarkupContainer("container");
391 container.setOutputMarkupId(true);
392 add(container);
393
394 dataProvider = new SortableDataProvider<>() {
395
396 private static final long serialVersionUID = 5291903859908641954L;
397
398 @Override
399 public Iterator<? extends T> iterator(final long first, final long count) {
400 return items.iterator();
401 }
402
403 @Override
404 public long size() {
405 return items.size();
406 }
407
408 @Override
409 public IModel<T> model(final T object) {
410 return new CompoundPropertyModel<>(object);
411 }
412 };
413
414 container.add(new AjaxFallbackDefaultDataTable<>(
415 "selectedObjects",
416 columns,
417 dataProvider,
418 Integer.MAX_VALUE).setMarkupId("selectedObjects").setVisible(!CollectionUtils.isEmpty(items)));
419
420 actionPanel = new ActionsPanel<>("actions", null);
421 container.add(actionPanel);
422 }
423 }