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.panels;
20
21 import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
22 import java.io.Serializable;
23 import java.util.List;
24 import java.util.stream.Collectors;
25 import org.apache.commons.lang3.SerializationUtils;
26 import org.apache.commons.lang3.tuple.Pair;
27 import org.apache.syncope.client.console.SyncopeConsoleSession;
28 import org.apache.syncope.client.console.SyncopeWebApplication;
29 import org.apache.syncope.client.console.audit.AuditHistoryModal;
30 import org.apache.syncope.client.console.commons.IdRepoConstants;
31 import org.apache.syncope.client.console.layout.AnyLayout;
32 import org.apache.syncope.client.console.layout.AnyLayoutUtils;
33 import org.apache.syncope.client.console.notifications.NotificationTasks;
34 import org.apache.syncope.client.console.pages.BasePage;
35 import org.apache.syncope.client.console.rest.AnyObjectRestClient;
36 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
37 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
38 import org.apache.syncope.client.console.rest.GroupRestClient;
39 import org.apache.syncope.client.console.rest.RoleRestClient;
40 import org.apache.syncope.client.console.rest.UserRestClient;
41 import org.apache.syncope.client.console.tasks.AnyPropagationTasks;
42 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
43 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
44 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
45 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
46 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
47 import org.apache.syncope.client.console.wizards.any.GroupWrapper;
48 import org.apache.syncope.client.lib.SyncopeClient;
49 import org.apache.syncope.client.ui.commons.Constants;
50 import org.apache.syncope.client.ui.commons.panels.ModalPanel;
51 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
52 import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
53 import org.apache.syncope.common.lib.AnyOperations;
54 import org.apache.syncope.common.lib.SyncopeClientException;
55 import org.apache.syncope.common.lib.SyncopeConstants;
56 import org.apache.syncope.common.lib.request.GroupUR;
57 import org.apache.syncope.common.lib.to.AnyObjectTO;
58 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
59 import org.apache.syncope.common.lib.to.AnyTypeTO;
60 import org.apache.syncope.common.lib.to.DerSchemaTO;
61 import org.apache.syncope.common.lib.to.GroupTO;
62 import org.apache.syncope.common.lib.to.PlainSchemaTO;
63 import org.apache.syncope.common.lib.to.ProvisioningResult;
64 import org.apache.syncope.common.lib.to.UserTO;
65 import org.apache.syncope.common.lib.types.AnyEntitlement;
66 import org.apache.syncope.common.lib.types.AnyTypeKind;
67 import org.apache.syncope.common.lib.types.AuditElements;
68 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
69 import org.apache.syncope.common.lib.types.ProvisionAction;
70 import org.apache.wicket.PageReference;
71 import org.apache.wicket.ajax.AjaxRequestTarget;
72 import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
73 import org.apache.wicket.event.Broadcast;
74 import org.apache.wicket.markup.html.WebPage;
75 import org.apache.wicket.markup.html.panel.Panel;
76 import org.apache.wicket.model.IModel;
77 import org.apache.wicket.model.Model;
78 import org.apache.wicket.model.ResourceModel;
79 import org.apache.wicket.model.StringResourceModel;
80 import org.apache.wicket.spring.injection.annot.SpringBean;
81
82 public class GroupDirectoryPanel extends AnyDirectoryPanel<GroupTO, GroupRestClient> {
83
84 private static final long serialVersionUID = -1100228004207271270L;
85
86 @SpringBean
87 protected AnyTypeRestClient anyTypeRestClient;
88
89 @SpringBean
90 protected AnyTypeClassRestClient anyTypeClassRestClient;
91
92 @SpringBean
93 protected RoleRestClient roleRestClient;
94
95 @SpringBean
96 protected UserRestClient userRestClient;
97
98 @SpringBean
99 protected GroupRestClient groupRestClient;
100
101 @SpringBean
102 protected AnyObjectRestClient anyObjectRestClient;
103
104 protected final BaseModal<Serializable> typeExtensionsModal = new BaseModal<>(Constants.OUTER);
105
106 protected final BaseModal<Serializable> membersModal = new BaseModal<>(Constants.OUTER);
107
108 protected final MembersTogglePanel membersTogglePanel;
109
110 protected GroupDirectoryPanel(final String id, final Builder builder, final boolean wizardInModal) {
111 super(id, builder, wizardInModal);
112
113 typeExtensionsModal.size(Modal.Size.Large);
114 addOuterObject(typeExtensionsModal);
115 setWindowClosedReloadCallback(typeExtensionsModal);
116 typeExtensionsModal.addSubmitButton();
117
118 addOuterObject(membersModal);
119 membersModal.size(Modal.Size.Large);
120
121 membersTogglePanel = new MembersTogglePanel(page.getPageReference()) {
122
123 private static final long serialVersionUID = -8765794727538618705L;
124
125 @Override
126 protected Serializable onApplyInternal(
127 final GroupTO groupTO, final String type, final AjaxRequestTarget target) {
128
129 AnyTypeTO anyType = anyTypeRestClient.read(type);
130
131 AnyLayout layout = AnyLayoutUtils.fetch(roleRestClient, anyTypeRestClient.list());
132
133 ModalPanel anyPanel = new AnyPanel.Builder<>(
134 layout.getAnyPanelClass(), BaseModal.CONTENT_ID, anyType, null, layout, false, pageRef).
135 build((id, anyTypeTO, realmTO, anyLayout, pr) -> {
136
137 final Panel panel;
138 if (AnyTypeKind.USER.name().equals(type)) {
139 String query = SyncopeClient.getUserSearchConditionBuilder().and(
140 SyncopeClient.getUserSearchConditionBuilder().inGroups(groupTO.getKey()),
141 SyncopeClient.getUserSearchConditionBuilder().
142 is(Constants.KEY_FIELD_NAME).notNullValue()).query();
143
144 panel = new UserDirectoryPanel.Builder(
145 anyTypeClassRestClient.list(
146 anyTypeTO.getClasses()), userRestClient, anyTypeTO.getKey(), pr).
147 setRealm(realm).
148 setFiltered(true).
149 setFiql(query).
150 disableCheckBoxes().
151 addNewItemPanelBuilder(
152 AnyLayoutUtils.newLayoutInfo(
153 new UserTO(),
154 anyTypeTO.getClasses(),
155 anyLayout.getUser(),
156 userRestClient,
157 pr), false).
158 setWizardInModal(false).build(id);
159
160 MetaDataRoleAuthorizationStrategy.authorize(
161 panel, WebPage.RENDER, IdRepoEntitlement.USER_SEARCH);
162 } else {
163 String query = SyncopeClient.getAnyObjectSearchConditionBuilder(type).and(
164 SyncopeClient.getUserSearchConditionBuilder().inGroups(groupTO.getKey()),
165 SyncopeClient.getUserSearchConditionBuilder().
166 is(Constants.KEY_FIELD_NAME).notNullValue()).query();
167
168 panel = new AnyObjectDirectoryPanel.Builder(
169 anyTypeClassRestClient.list(
170 anyTypeTO.getClasses()), anyObjectRestClient, anyTypeTO.getKey(), pr).
171 setRealm(realm).
172 setFiltered(true).
173 setFiql(query).
174 disableCheckBoxes().
175 addNewItemPanelBuilder(AnyLayoutUtils.newLayoutInfo(
176 new AnyObjectTO(),
177 anyTypeTO.getClasses(),
178 layout.getAnyObjects().get(type),
179 anyObjectRestClient,
180 pr), false).
181 setWizardInModal(false).build(id);
182
183 MetaDataRoleAuthorizationStrategy.authorize(
184 panel, WebPage.RENDER, AnyEntitlement.SEARCH.getFor(anyTypeTO.getKey()));
185 }
186
187 return panel;
188 });
189
190 membersModal.header(new StringResourceModel(
191 "group.members",
192 GroupDirectoryPanel.this,
193 Model.of(Pair.of(groupTO, new ResourceModel("anyType." + type, type).getObject()))));
194
195 membersModal.setContent(anyPanel);
196 membersModal.show(true);
197 target.add(membersModal);
198
199 return null;
200 }
201 };
202
203 addOuterObject(membersTogglePanel);
204 }
205
206 @Override
207 protected String paginatorRowsKey() {
208 return IdRepoConstants.PREF_GROUP_PAGINATOR_ROWS;
209 }
210
211 @Override
212 protected String[] getDefaultAttributeSelection() {
213 return GroupDisplayAttributesModalPanel.DEFAULT_SELECTION;
214 }
215
216 @Override
217 public ActionsPanel<Serializable> getHeader(final String componentId) {
218 final ActionsPanel<Serializable> panel = super.getHeader(componentId);
219
220 panel.add(new ActionLink<>() {
221
222 private static final long serialVersionUID = -7978723352517770644L;
223
224 @Override
225 public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
226 target.add(displayAttributeModal.setContent(new GroupDisplayAttributesModalPanel<>(
227 displayAttributeModal,
228 page.getPageReference(),
229 plainSchemas.stream().map(PlainSchemaTO::getKey).collect(Collectors.toList()),
230 derSchemas.stream().map(DerSchemaTO::getKey).collect(Collectors.toList()))));
231 displayAttributeModal.header(new ResourceModel("any.attr.display"));
232 displayAttributeModal.show(true);
233 }
234 }, ActionType.CHANGE_VIEW, IdRepoEntitlement.GROUP_READ).hideLabel();
235 return panel;
236 }
237
238 @Override
239 public ActionsPanel<GroupTO> getActions(final IModel<GroupTO> model) {
240 ActionsPanel<GroupTO> actions = super.getActions(model);
241
242 actions.add(new ActionLink<>() {
243
244 private static final long serialVersionUID = -7978723352517770644L;
245
246 @Override
247 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
248 send(GroupDirectoryPanel.this, Broadcast.EXACT,
249 new AjaxWizard.EditItemActionEvent<>(new GroupWrapper(
250 restClient.read(model.getObject().getKey())), target));
251 }
252 }, ActionType.EDIT,
253 String.format("%s,%s", IdRepoEntitlement.GROUP_READ, IdRepoEntitlement.GROUP_UPDATE)).
254 setRealms(realm, model.getObject().getDynRealms());
255
256 actions.add(new ActionLink<>() {
257
258 private static final long serialVersionUID = 6242834621660352855L;
259
260 @Override
261 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
262 target.add(typeExtensionsModal.setContent(new TypeExtensionDirectoryPanel(
263 typeExtensionsModal, model.getObject(), pageRef)));
264 typeExtensionsModal.header(new StringResourceModel("typeExtensions", model));
265 typeExtensionsModal.show(true);
266 }
267 }, ActionType.TYPE_EXTENSIONS, IdRepoEntitlement.GROUP_UPDATE).
268 setRealms(realm, model.getObject().getDynRealms());
269
270 actions.add(new ActionLink<>() {
271
272 private static final long serialVersionUID = -7978723352517770645L;
273
274 @Override
275 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
276 membersTogglePanel.setHeader(target, model.getObject().getName());
277 membersTogglePanel.setTargetObject(model.getObject());
278 membersTogglePanel.toggle(target, model.getObject(), true);
279 }
280
281 @Override
282 public boolean isIndicatorEnabled() {
283 return false;
284 }
285 }, ActionType.MEMBERS,
286 String.format("%s,%s", IdRepoEntitlement.GROUP_READ, IdRepoEntitlement.GROUP_UPDATE)).
287 setRealms(realm, model.getObject().getDynRealms());
288
289 ActionLink<GroupTO> provisionMembers = new ActionLink<GroupTO>() {
290
291 private static final long serialVersionUID = -7978723352517770644L;
292
293 @Override
294 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
295 try {
296 groupRestClient.provisionMembers(model.getObject().getKey(), ProvisionAction.PROVISION);
297 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
298 target.add(container);
299 } catch (SyncopeClientException e) {
300 LOG.error("While provisioning members of group {}", model.getObject().getKey(), e);
301 SyncopeConsoleSession.get().onException(e);
302 }
303 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
304 }
305 }.confirmMessage("confirmProvisionMembers");
306 actions.add(
307 provisionMembers,
308 ActionType.PROVISION_MEMBERS,
309 String.format("%s,%s", IdRepoEntitlement.TASK_CREATE, IdRepoEntitlement.TASK_EXECUTE),
310 true).setRealm(realm);
311
312 ActionLink<GroupTO> deprovisionMembers = new ActionLink<GroupTO>() {
313
314 private static final long serialVersionUID = -7978723352517770644L;
315
316 @Override
317 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
318 try {
319 groupRestClient.provisionMembers(model.getObject().getKey(), ProvisionAction.DEPROVISION);
320 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
321 target.add(container);
322 } catch (SyncopeClientException e) {
323 LOG.error("While provisioning members of group {}", model.getObject().getKey(), e);
324 SyncopeConsoleSession.get().onException(e);
325 }
326 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
327 }
328 }.confirmMessage("confirmDeprovisionMembers");
329 actions.add(
330 deprovisionMembers,
331 ActionType.DEPROVISION_MEMBERS,
332 String.format("%s,%s", IdRepoEntitlement.TASK_CREATE, IdRepoEntitlement.TASK_EXECUTE),
333 true).setRealm(realm);
334
335 SyncopeWebApplication.get().getAnyDirectoryPanelAdditionalActionLinksProvider().get(
336 model.getObject(),
337 realm,
338 altDefaultModal,
339 getString("any.edit", new Model<>(new GroupWrapper(model.getObject()))),
340 this,
341 pageRef).forEach(actions::add);
342
343 actions.add(new ActionLink<>() {
344
345 private static final long serialVersionUID = -7978723352517770644L;
346
347 @Override
348 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
349 target.add(utilityModal.setContent(new AnyPropagationTasks(
350 utilityModal, AnyTypeKind.GROUP, model.getObject().getKey(), pageRef)));
351 utilityModal.header(new StringResourceModel("any.propagation.tasks", model));
352 utilityModal.show(true);
353 }
354 }, ActionType.PROPAGATION_TASKS, IdRepoEntitlement.TASK_LIST);
355
356 actions.add(new ActionLink<>() {
357
358 private static final long serialVersionUID = -7978723352517770644L;
359
360 @Override
361 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
362 target.add(utilityModal.setContent(
363 new NotificationTasks(AnyTypeKind.GROUP, model.getObject().getKey(), pageRef)));
364 utilityModal.header(new StringResourceModel("any.notification.tasks", model));
365 utilityModal.show(true);
366 }
367 }, ActionType.NOTIFICATION_TASKS, IdRepoEntitlement.TASK_LIST);
368
369 actions.add(new ActionLink<>() {
370
371 private static final long serialVersionUID = -2878723352517770644L;
372
373 @Override
374 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
375 model.setObject(restClient.read(model.getObject().getKey()));
376 target.add(altDefaultModal.setContent(new AuditHistoryModal<>(
377 AuditElements.EventCategoryType.LOGIC,
378 "GroupLogic",
379 model.getObject(),
380 IdRepoEntitlement.GROUP_UPDATE,
381 auditRestClient) {
382
383 private static final long serialVersionUID = -5819724478921691835L;
384
385 @Override
386 protected void restore(final String json, final AjaxRequestTarget target) {
387 GroupTO original = model.getObject();
388 try {
389 GroupTO updated = MAPPER.readValue(json, GroupTO.class);
390 GroupUR updateReq = AnyOperations.diff(updated, original, false);
391 ProvisioningResult<GroupTO> result =
392 restClient.update(original.getETagValue(), updateReq);
393 model.getObject().setLastChangeDate(result.getEntity().getLastChangeDate());
394
395 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
396 target.add(container);
397 } catch (Exception e) {
398 LOG.error("While restoring group {}", model.getObject().getKey(), e);
399 SyncopeConsoleSession.get().onException(e);
400 }
401 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
402 }
403 }));
404
405 altDefaultModal.header(new Model<>(
406 getString("auditHistory.title", new Model<>(new GroupWrapper(model.getObject())))));
407
408 altDefaultModal.show(true);
409 }
410 }, ActionType.VIEW_AUDIT_HISTORY,
411 String.format("%s,%s", IdRepoEntitlement.GROUP_READ, IdRepoEntitlement.AUDIT_LIST)).
412 setRealms(realm, model.getObject().getDynRealms());
413
414 actions.add(new ActionLink<>() {
415
416 private static final long serialVersionUID = 6242834621660352855L;
417
418 @Override
419 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
420 GroupTO clone = SerializationUtils.clone(model.getObject());
421 clone.setKey(null);
422 send(GroupDirectoryPanel.this, Broadcast.EXACT,
423 new AjaxWizard.NewItemActionEvent<>(new GroupWrapper(clone), target));
424 }
425
426 @Override
427 protected boolean statusCondition(final GroupTO modelObject) {
428 return realm.startsWith(SyncopeConstants.ROOT_REALM);
429 }
430 }, ActionType.CLONE, IdRepoEntitlement.GROUP_CREATE).setRealm(realm);
431
432 actions.add(new ActionLink<>() {
433
434 private static final long serialVersionUID = -7978723352517770644L;
435
436 @Override
437 public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
438 try {
439 restClient.delete(model.getObject().getETagValue(), model.getObject().getKey());
440 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
441 target.add(container);
442 } catch (SyncopeClientException e) {
443 LOG.error("While deleting group {}", model.getObject().getKey(), e);
444 SyncopeConsoleSession.get().onException(e);
445 }
446 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
447 }
448
449 @Override
450 protected boolean statusCondition(final GroupTO modelObject) {
451 return realm.startsWith(SyncopeConstants.ROOT_REALM);
452 }
453 }, ActionType.DELETE, IdRepoEntitlement.GROUP_DELETE, true).setRealm(realm);
454
455 return actions;
456 }
457
458 public static class Builder extends AnyDirectoryPanel.Builder<GroupTO, GroupRestClient> {
459
460 private static final long serialVersionUID = 3844281520756293159L;
461
462 public Builder(
463 final List<AnyTypeClassTO> anyTypeClassTOs,
464 final GroupRestClient groupRestClient,
465 final String type,
466 final PageReference pageRef) {
467
468 super(anyTypeClassTOs, groupRestClient, type, pageRef);
469 setShowResultPage(true);
470 }
471
472 @Override
473 protected WizardMgtPanel<AnyWrapper<GroupTO>> newInstance(final String id, final boolean wizardInModal) {
474 return new GroupDirectoryPanel(id, this, wizardInModal);
475 }
476 }
477 }