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.events;
20  
21  import java.util.Comparator;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Set;
25  import java.util.stream.Collectors;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.commons.lang3.tuple.Pair;
28  import org.apache.syncope.client.console.events.SelectedEventsPanel.EventSelectionChanged;
29  import org.apache.syncope.client.console.events.SelectedEventsPanel.InspectSelectedEvent;
30  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
31  import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
32  import org.apache.syncope.client.ui.commons.Constants;
33  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
34  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
35  import org.apache.syncope.common.lib.audit.EventCategory;
36  import org.apache.syncope.common.lib.types.AuditElements;
37  import org.apache.syncope.common.lib.types.AuditElements.EventCategoryType;
38  import org.apache.syncope.common.lib.types.AuditLoggerName;
39  import org.apache.wicket.ajax.AjaxRequestTarget;
40  import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
41  import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
42  import org.apache.wicket.event.Broadcast;
43  import org.apache.wicket.event.IEvent;
44  import org.apache.wicket.markup.html.WebMarkupContainer;
45  import org.apache.wicket.markup.html.basic.Label;
46  import org.apache.wicket.markup.html.form.IChoiceRenderer;
47  import org.apache.wicket.markup.html.panel.Panel;
48  import org.apache.wicket.model.IModel;
49  import org.apache.wicket.model.Model;
50  import org.apache.wicket.model.PropertyModel;
51  import org.apache.wicket.model.ResourceModel;
52  
53  public abstract class EventCategoryPanel extends Panel {
54  
55      private static final long serialVersionUID = 6429053774964787734L;
56  
57      protected static class ChangeCategoryEvent {
58  
59          protected final AjaxRequestTarget target;
60  
61          protected final Panel changedPanel;
62  
63          ChangeCategoryEvent(final AjaxRequestTarget target, final Panel changedPanel) {
64              this.target = target;
65              this.changedPanel = changedPanel;
66          }
67  
68          public AjaxRequestTarget getTarget() {
69              return target;
70          }
71  
72          public Panel getChangedPanel() {
73              return changedPanel;
74          }
75      }
76  
77      protected static List<String> filter(
78              final List<EventCategory> categories,
79              final EventCategoryType type) {
80  
81          return categories.stream().
82                  filter(c -> type == c.getType() && StringUtils.isNotEmpty(c.getCategory())).
83                  map(EventCategory::getCategory).
84                  distinct().
85                  sorted().
86                  collect(Collectors.toList());
87      }
88  
89      protected static List<String> filter(
90              final List<EventCategory> categories,
91              final EventCategoryType type,
92              final String category) {
93  
94          return categories.stream().
95                  filter(c -> type == c.getType() && StringUtils.equals(category, c.getCategory())).
96                  map(EventCategory::getSubcategory).
97                  distinct().
98                  sorted().
99                  collect(Collectors.toList());
100     }
101 
102     private final List<EventCategory> eventCategories;
103 
104     private final EventCategory eventCategory = new EventCategory();
105 
106     private final WebMarkupContainer categoryContainer;
107 
108     private final WebMarkupContainer eventsContainer;
109 
110     private final SelectedEventsPanel selectedEventsPanel;
111 
112     private final AjaxDropDownChoicePanel<EventCategoryType> type;
113 
114     private final AjaxDropDownChoicePanel<String> category;
115 
116     private final AjaxDropDownChoicePanel<String> subcategory;
117 
118     private final AjaxTextFieldPanel custom;
119 
120     private final ActionsPanel<EventCategory> actionsPanel;
121 
122     private final IModel<List<String>> model;
123 
124     public EventCategoryPanel(
125             final String id,
126             final List<EventCategory> eventCategories,
127             final IModel<List<String>> model) {
128 
129         super(id);
130 
131         this.model = model;
132         selectedEventsPanel = new SelectedEventsPanel("selectedEventsPanel", model);
133         add(selectedEventsPanel);
134 
135         this.eventCategories = eventCategories;
136 
137         categoryContainer = new WebMarkupContainer("categoryContainer");
138         categoryContainer.setOutputMarkupId(true);
139         add(categoryContainer);
140 
141         eventsContainer = new WebMarkupContainer("eventsContainer");
142         eventsContainer.setOutputMarkupId(true);
143         add(eventsContainer);
144 
145         authorizeList();
146         authorizeChanges();
147 
148         type = new AjaxDropDownChoicePanel<>(
149                 "type",
150                 "type",
151                 new PropertyModel<>(eventCategory, "type"),
152                 false);
153         type.setChoices(eventCategories.stream().
154                 map(EventCategory::getType).distinct().
155                 sorted(Comparator.comparing(EventCategoryType::name)).
156                 collect(Collectors.toList()));
157         type.setChoiceRenderer(new IChoiceRenderer<>() {
158 
159             private static final long serialVersionUID = 2317134950949778735L;
160 
161             @Override
162             public String getDisplayValue(final EventCategoryType eventCategoryType) {
163                 return eventCategoryType.name();
164             }
165 
166             @Override
167             public String getIdValue(final EventCategoryType eventCategoryType, final int i) {
168                 return eventCategoryType.name();
169             }
170 
171             @Override
172             public EventCategoryType getObject(
173                     final String id,
174                     final IModel<? extends List<? extends EventCategoryType>> choices) {
175 
176                 return choices.getObject().stream().filter(object -> object.name().equals(id)).findAny().orElse(null);
177             }
178         });
179         categoryContainer.add(type);
180 
181         type.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
182 
183             private static final long serialVersionUID = -1107858522700306810L;
184 
185             @Override
186             protected void onUpdate(final AjaxRequestTarget target) {
187                 send(EventCategoryPanel.this, Broadcast.EXACT, new ChangeCategoryEvent(target, type));
188             }
189         });
190 
191         category = new AjaxDropDownChoicePanel<>(
192                 "category",
193                 "category",
194                 new PropertyModel<>(eventCategory, "category"),
195                 false);
196         category.setChoices(filter(eventCategories, type.getModelObject()));
197         categoryContainer.add(category);
198 
199         category.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
200 
201             private static final long serialVersionUID = -1107858522700306811L;
202 
203             @Override
204             protected void onUpdate(final AjaxRequestTarget target) {
205                 send(EventCategoryPanel.this, Broadcast.EXACT, new ChangeCategoryEvent(target, category));
206             }
207         });
208 
209         subcategory = new AjaxDropDownChoicePanel<>(
210                 "subcategory",
211                 "subcategory",
212                 new PropertyModel<>(eventCategory, "subcategory"),
213                 false);
214         subcategory.setChoices(filter(eventCategories, type.getModelObject(), category.getModelObject()));
215         categoryContainer.add(subcategory);
216 
217         subcategory.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
218 
219             private static final long serialVersionUID = -1107858522700306812L;
220 
221             @Override
222             protected void onUpdate(final AjaxRequestTarget target) {
223                 send(EventCategoryPanel.this, Broadcast.EXACT, new ChangeCategoryEvent(target, subcategory));
224             }
225         });
226 
227         categoryContainer.add(new Label("customLabel", new ResourceModel("custom", "custom")).setVisible(false));
228 
229         custom = new AjaxTextFieldPanel("custom", "custom", new Model<>(null));
230         custom.setVisible(false);
231         custom.setEnabled(false);
232 
233         categoryContainer.add(custom.hideLabel());
234 
235         actionsPanel = new ActionsPanel<>("customActions", null);
236         actionsPanel.add(new ActionLink<>() {
237 
238             private static final long serialVersionUID = -3722207913631435501L;
239 
240             @Override
241             public void onClick(final AjaxRequestTarget target, final EventCategory ignore) {
242                 if (StringUtils.isNotBlank(custom.getModelObject())) {
243                     Pair<EventCategory, AuditElements.Result> parsed =
244                             AuditLoggerName.parseEventCategory(custom.getModelObject());
245 
246                     AuditLoggerName auditLoggerName = new AuditLoggerName(
247                             parsed.getLeft().getType(),
248                             null,
249                             null,
250                             parsed.getLeft().getEvents().isEmpty()
251                             ? StringUtils.EMPTY : parsed.getLeft().getEvents().iterator().next(),
252                             parsed.getRight());
253 
254                     custom.setModelObject(StringUtils.EMPTY);
255                     send(EventCategoryPanel.this.getPage(), Broadcast.BREADTH, new EventSelectionChanged(
256                             target,
257                             Set.of(auditLoggerName),
258                             Set.of()));
259                     target.add(categoryContainer);
260                 }
261             }
262         }, ActionLink.ActionType.CREATE, StringUtils.EMPTY).hideLabel();
263         actionsPanel.add(new ActionLink<>() {
264 
265             private static final long serialVersionUID = -3722207913631435521L;
266 
267             @Override
268             public void onClick(final AjaxRequestTarget target, final EventCategory ignore) {
269                 if (StringUtils.isNotBlank(custom.getModelObject())) {
270                     Pair<EventCategory, AuditElements.Result> parsed =
271                             AuditLoggerName.parseEventCategory(custom.getModelObject());
272 
273                     AuditLoggerName auditLoggerName = new AuditLoggerName(
274                             parsed.getLeft().getType(),
275                             null,
276                             null,
277                             parsed.getLeft().getEvents().isEmpty()
278                             ? StringUtils.EMPTY : parsed.getLeft().getEvents().iterator().next(),
279                             parsed.getRight());
280 
281                     custom.setModelObject(StringUtils.EMPTY);
282                     send(EventCategoryPanel.this.getPage(), Broadcast.BREADTH, new EventSelectionChanged(
283                             target,
284                             Set.of(),
285                             Set.of(auditLoggerName)));
286                     target.add(categoryContainer);
287                 }
288             }
289         }, ActionLink.ActionType.DELETE, StringUtils.EMPTY, true).hideLabel();
290 
291         categoryContainer.add(actionsPanel);
292 
293         actionsPanel.setVisible(false);
294         actionsPanel.setEnabled(false);
295         actionsPanel.setMarkupId("inline-actions");
296 
297         eventsContainer.add(new EventSelectionPanel("eventsPanel", eventCategory, model) {
298 
299             private static final long serialVersionUID = 3513194801190026082L;
300 
301             @Override
302             protected void onEventAction(final IEvent<?> event) {
303                 EventCategoryPanel.this.onEventAction(event);
304             }
305         });
306     }
307 
308     @Override
309     public void onEvent(final IEvent<?> event) {
310         if (event.getPayload() instanceof ChangeCategoryEvent) {
311             // update objects ....
312             eventCategory.getEvents().clear();
313 
314             ChangeCategoryEvent change = (ChangeCategoryEvent) event.getPayload();
315 
316             switch (change.getChangedPanel().getId()) {
317                 case "type":
318                     eventCategory.setType(type.getModelObject());
319                     eventCategory.setCategory(null);
320                     eventCategory.setSubcategory(null);
321                     if (type.getModelObject() == EventCategoryType.CUSTOM
322                             || type.getModelObject() == EventCategoryType.WA) {
323 
324                         category.setChoices(List.of());
325                         subcategory.setChoices(List.of());
326                         category.setEnabled(false);
327                         subcategory.setEnabled(false);
328                         custom.setVisible(true);
329                         custom.setEnabled(true);
330                         actionsPanel.setVisible(true);
331                         actionsPanel.setEnabled(true);
332                     } else {
333                         category.setChoices(filter(eventCategories, type.getModelObject()));
334                         subcategory.setChoices(List.of());
335                         category.setEnabled(true);
336                         subcategory.setEnabled(true);
337                         custom.setVisible(false);
338                         custom.setEnabled(false);
339                         actionsPanel.setVisible(false);
340                         actionsPanel.setEnabled(false);
341                     }
342                     change.getTarget().add(categoryContainer);
343                     break;
344 
345                 case "category":
346                     subcategory.setChoices(
347                             filter(eventCategories, type.getModelObject(), category.getModelObject()));
348                     eventCategory.setCategory(category.getModelObject());
349                     eventCategory.setSubcategory(null);
350                     change.getTarget().add(categoryContainer);
351                     break;
352 
353                 default:
354                     eventCategory.setSubcategory(subcategory.getModelObject());
355                     break;
356             }
357 
358             updateEventsContainer(change.getTarget());
359         } else if (event.getPayload() instanceof InspectSelectedEvent) {
360             // update objects ....
361             eventCategory.getEvents().clear();
362 
363             InspectSelectedEvent inspectSelectedEvent = (InspectSelectedEvent) event.getPayload();
364 
365             Pair<EventCategory, AuditElements.Result> categoryEvent =
366                     AuditLoggerName.parseEventCategory(inspectSelectedEvent.getEvent());
367 
368             eventCategory.setType(categoryEvent.getLeft().getType());
369             category.setChoices(filter(eventCategories, type.getModelObject()));
370 
371             eventCategory.setCategory(categoryEvent.getLeft().getCategory());
372             subcategory.setChoices(filter(eventCategories, type.getModelObject(), category.getModelObject()));
373 
374             eventCategory.setSubcategory(categoryEvent.getLeft().getSubcategory());
375 
376             if (categoryEvent.getLeft().getType() == EventCategoryType.CUSTOM
377                     || categoryEvent.getLeft().getType() == EventCategoryType.WA) {
378 
379                 custom.setModelObject(AuditLoggerName.buildEvent(
380                         categoryEvent.getLeft().getType(),
381                         categoryEvent.getLeft().getCategory(),
382                         categoryEvent.getLeft().getSubcategory(),
383                         categoryEvent.getLeft().getEvents().isEmpty()
384                         ? StringUtils.EMPTY : categoryEvent.getLeft().getEvents().iterator().next(),
385                         categoryEvent.getRight()));
386 
387                 category.setEnabled(false);
388                 subcategory.setEnabled(false);
389                 custom.setVisible(true);
390                 custom.setEnabled(true);
391                 actionsPanel.setVisible(true);
392                 actionsPanel.setEnabled(true);
393             } else {
394                 category.setEnabled(true);
395                 subcategory.setEnabled(true);
396                 custom.setVisible(false);
397                 custom.setEnabled(false);
398                 actionsPanel.setVisible(false);
399                 actionsPanel.setEnabled(false);
400             }
401 
402             inspectSelectedEvent.getTarget().add(categoryContainer);
403             updateEventsContainer(inspectSelectedEvent.getTarget());
404         }
405     }
406 
407     /**
408      * To be extended in order to add actions on events.
409      *
410      * @param event event.
411      */
412     protected void onEventAction(final IEvent<?> event) {
413         // nothing by default
414     }
415 
416     protected abstract List<String> getListAuthRoles();
417 
418     protected void authorizeList() {
419         getListAuthRoles().forEach(r -> MetaDataRoleAuthorizationStrategy.authorize(selectedEventsPanel, RENDER, r));
420     }
421 
422     protected abstract List<String> getChangeAuthRoles();
423 
424     protected void authorizeChanges() {
425         getChangeAuthRoles().forEach(r -> {
426             MetaDataRoleAuthorizationStrategy.authorize(categoryContainer, RENDER, r);
427             MetaDataRoleAuthorizationStrategy.authorize(eventsContainer, RENDER, r);
428         });
429     }
430 
431     protected void updateEventsContainer(final AjaxRequestTarget target) {
432         for (Iterator<EventCategory> itor = eventCategories.iterator();
433                 itor.hasNext() && eventCategory.getEvents().isEmpty();) {
434 
435             EventCategory ec = itor.next();
436             if (ec.getType() == eventCategory.getType()
437                     && StringUtils.equals(ec.getCategory(), eventCategory.getCategory())
438                     && StringUtils.equals(ec.getSubcategory(), eventCategory.getSubcategory())) {
439 
440                 eventCategory.getEvents().addAll(ec.getEvents());
441             }
442         }
443 
444         eventsContainer.addOrReplace(new EventSelectionPanel("eventsPanel", eventCategory, model) {
445 
446             private static final long serialVersionUID = 3513194801190026082L;
447 
448             @Override
449             public void onEventAction(final IEvent<?> event) {
450                 EventCategoryPanel.this.onEventAction(event);
451             }
452         });
453         target.add(eventsContainer);
454     }
455 }