View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }