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.topology;
20  
21  import com.fasterxml.jackson.databind.json.JsonMapper;
22  import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
23  import java.io.Serializable;
24  import java.text.MessageFormat;
25  import java.util.Optional;
26  import org.apache.syncope.client.console.SyncopeConsoleSession;
27  import org.apache.syncope.client.console.audit.AuditHistoryModal;
28  import org.apache.syncope.client.console.panels.ConnObjects;
29  import org.apache.syncope.client.console.panels.TogglePanel;
30  import org.apache.syncope.client.console.rest.AuditRestClient;
31  import org.apache.syncope.client.console.rest.ConnectorRestClient;
32  import org.apache.syncope.client.console.rest.ResourceRestClient;
33  import org.apache.syncope.client.console.status.ResourceStatusModal;
34  import org.apache.syncope.client.console.tasks.PropagationTasks;
35  import org.apache.syncope.client.console.tasks.PullTasks;
36  import org.apache.syncope.client.console.tasks.PushTasks;
37  import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
38  import org.apache.syncope.client.console.wicket.markup.html.form.IndicatingOnConfirmAjaxLink;
39  import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
40  import org.apache.syncope.client.console.wizards.resources.ResourceProvision;
41  import org.apache.syncope.client.console.wizards.resources.ResourceProvisionPanel;
42  import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
43  import org.apache.syncope.client.ui.commons.Constants;
44  import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
45  import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
46  import org.apache.syncope.common.lib.SyncopeClientException;
47  import org.apache.syncope.common.lib.to.ConnInstanceTO;
48  import org.apache.syncope.common.lib.to.Provision;
49  import org.apache.syncope.common.lib.to.ResourceTO;
50  import org.apache.syncope.common.lib.types.AuditElements;
51  import org.apache.syncope.common.lib.types.IdMEntitlement;
52  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
53  import org.apache.wicket.PageReference;
54  import org.apache.wicket.ajax.AjaxRequestTarget;
55  import org.apache.wicket.ajax.markup.html.AjaxLink;
56  import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
57  import org.apache.wicket.event.Broadcast;
58  import org.apache.wicket.event.IEvent;
59  import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
60  import org.apache.wicket.markup.html.WebMarkupContainer;
61  import org.apache.wicket.markup.html.panel.Fragment;
62  import org.apache.wicket.model.CompoundPropertyModel;
63  import org.apache.wicket.model.IModel;
64  import org.apache.wicket.model.Model;
65  import org.apache.wicket.model.StringResourceModel;
66  import org.apache.wicket.spring.injection.annot.SpringBean;
67  
68  public class TopologyTogglePanel extends TogglePanel<Serializable> {
69  
70      private static final long serialVersionUID = -2025535531121434056L;
71  
72      protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
73  
74      @SpringBean
75      protected ConnectorRestClient connectorRestClient;
76  
77      @SpringBean
78      protected ResourceRestClient resourceRestClient;
79  
80      @SpringBean
81      protected AuditRestClient auditRestClient;
82  
83      protected final WebMarkupContainer container;
84  
85      protected final BaseModal<Serializable> propTaskModal;
86  
87      protected final BaseModal<Serializable> schedTaskModal;
88  
89      protected final BaseModal<Serializable> provisionModal;
90  
91      protected final BaseModal<Serializable> historyModal;
92  
93      public TopologyTogglePanel(final String id, final PageReference pageRef) {
94          super(id, "topologyTogglePanel", pageRef);
95  
96          modal.size(Modal.Size.Large);
97          setFooterVisibility(false);
98  
99          propTaskModal = new BaseModal<>(Constants.OUTER);
100         propTaskModal.size(Modal.Size.Large);
101         addOuterObject(propTaskModal);
102 
103         schedTaskModal = new BaseModal<>(Constants.OUTER) {
104 
105             private static final long serialVersionUID = 389935548143327858L;
106 
107             @Override
108             protected void onConfigure() {
109                 super.onConfigure();
110                 setFooterVisible(false);
111             }
112         };
113         schedTaskModal.size(Modal.Size.Large);
114         addOuterObject(schedTaskModal);
115 
116         provisionModal = new BaseModal<>(Constants.OUTER);
117         provisionModal.size(Modal.Size.Large);
118         provisionModal.addSubmitButton();
119         addOuterObject(provisionModal);
120 
121         historyModal = new BaseModal<>(Constants.OUTER);
122         historyModal.size(Modal.Size.Large);
123         addOuterObject(historyModal);
124 
125         container = new WebMarkupContainer("container");
126         container.setOutputMarkupPlaceholderTag(true);
127         addInnerObject(container);
128 
129         container.add(getEmptyFragment());
130     }
131 
132     public void toggleWithContent(final AjaxRequestTarget target, final TopologyNode node) {
133         setHeader(target, node.getDisplayName());
134 
135         modal.setWindowClosedCallback(t -> {
136             modal.show(false);
137             send(pageRef.getPage(), Broadcast.DEPTH, new UpdateEvent(node.getKey(), t));
138         });
139 
140         switch (node.getKind()) {
141             case SYNCOPE:
142                 container.addOrReplace(getSyncopeFragment(pageRef));
143                 break;
144             case CONNECTOR_SERVER:
145                 container.addOrReplace(getLocationFragment(node, pageRef));
146                 break;
147             case FS_PATH:
148                 container.addOrReplace(getLocationFragment(node, pageRef));
149                 break;
150             case CONNECTOR:
151                 container.addOrReplace(getConnectorFragment(node, pageRef));
152                 break;
153             case RESOURCE:
154                 container.addOrReplace(getResourceFragment(node, pageRef));
155                 break;
156             default:
157                 container.addOrReplace(getEmptyFragment());
158         }
159 
160         target.add(container);
161 
162         toggle(target, node, true);
163     }
164 
165     @Override
166     protected String getTargetKey(final Serializable modelObject) {
167         String key = super.getTargetKey(modelObject);
168         if (modelObject instanceof ResourceProvision) {
169             key = ((ResourceProvision) modelObject).getKey();
170         } else if (modelObject instanceof TopologyNode) {
171             key = ((TopologyNode) modelObject).getKey();
172         }
173         return key;
174     }
175 
176     private Fragment getEmptyFragment() {
177         return new Fragment("actions", "emptyFragment", this);
178     }
179 
180     private Fragment getSyncopeFragment(final PageReference pageRef) {
181         Fragment fragment = new Fragment("actions", "syncopeActions", this);
182 
183         AjaxLink<String> reload = new IndicatingOnConfirmAjaxLink<>("reload", "connectors.confirm.reload", true) {
184 
185             private static final long serialVersionUID = -2075933173666007020L;
186 
187             @Override
188             public void onClick(final AjaxRequestTarget target) {
189                 try {
190                     connectorRestClient.reload();
191                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
192                 } catch (Exception e) {
193                     LOG.error("While reloading all connectors", e);
194                     SyncopeConsoleSession.get().onException(e);
195                 }
196                 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
197             }
198         };
199         fragment.add(reload);
200         MetaDataRoleAuthorizationStrategy.authorize(reload, RENDER, IdMEntitlement.CONNECTOR_RELOAD);
201 
202         return fragment;
203     }
204 
205     private Fragment getLocationFragment(final TopologyNode node, final PageReference pageRef) {
206         Fragment fragment = new Fragment("actions", "locationActions", this);
207 
208         AjaxLink<String> create = new IndicatingAjaxLink<>("create") {
209 
210             private static final long serialVersionUID = 3776750333491622263L;
211 
212             @Override
213             public void onClick(final AjaxRequestTarget target) {
214                 final ConnInstanceTO modelObject = new ConnInstanceTO();
215                 modelObject.setLocation(node.getKey());
216 
217                 final IModel<ConnInstanceTO> model = new CompoundPropertyModel<>(modelObject);
218                 modal.setFormModel(model);
219 
220                 target.add(modal.setContent(new ConnectorWizardBuilder(modelObject, connectorRestClient, pageRef).
221                         build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE)));
222 
223                 modal.header(new Model<>(MessageFormat.format(getString("connector.new"), node.getKey())));
224                 modal.show(true);
225             }
226 
227             @Override
228             public String getAjaxIndicatorMarkupId() {
229                 return Constants.VEIL_INDICATOR_MARKUP_ID;
230             }
231         };
232         fragment.add(create);
233         MetaDataRoleAuthorizationStrategy.authorize(create, RENDER, IdMEntitlement.CONNECTOR_CREATE);
234 
235         return fragment;
236     }
237 
238     private Fragment getConnectorFragment(final TopologyNode node, final PageReference pageRef) {
239         Fragment fragment = new Fragment("actions", "connectorActions", this);
240 
241         AjaxLink<String> delete = new IndicatingOnConfirmAjaxLink<>("delete", Constants.CONFIRM_DELETE, true) {
242 
243             private static final long serialVersionUID = 3776750333491622263L;
244 
245             @Override
246             public void onClick(final AjaxRequestTarget target) {
247                 try {
248                     connectorRestClient.delete(String.class.cast(node.getKey()));
249                     target.appendJavaScript(String.format("jsPlumb.remove('%s');", node.getKey()));
250                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
251                     toggle(target, false);
252                 } catch (SyncopeClientException e) {
253                     LOG.error("While deleting resource {}", node.getKey(), e);
254                     SyncopeConsoleSession.get().onException(e);
255                 }
256                 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
257             }
258         };
259         MetaDataRoleAuthorizationStrategy.authorize(delete, RENDER, IdMEntitlement.CONNECTOR_DELETE);
260         fragment.add(delete);
261 
262         AjaxLink<String> create = new IndicatingAjaxLink<>("create") {
263 
264             private static final long serialVersionUID = 3776750333491622263L;
265 
266             @Override
267             public void onClick(final AjaxRequestTarget target) {
268                 final ResourceTO modelObject = new ResourceTO();
269                 modelObject.setConnector(String.class.cast(node.getKey()));
270                 modelObject.setConnectorDisplayName(node.getDisplayName());
271 
272                 final IModel<ResourceTO> model = new CompoundPropertyModel<>(modelObject);
273                 modal.setFormModel(model);
274 
275                 target.add(modal.setContent(new ResourceWizardBuilder(
276                         modelObject, resourceRestClient, connectorRestClient, pageRef).
277                         build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE)));
278 
279                 modal.header(new Model<>(MessageFormat.format(getString("resource.new"), node.getKey())));
280                 modal.show(true);
281             }
282 
283             @Override
284             public String getAjaxIndicatorMarkupId() {
285                 return Constants.VEIL_INDICATOR_MARKUP_ID;
286             }
287 
288         };
289         MetaDataRoleAuthorizationStrategy.authorize(create, RENDER, IdMEntitlement.RESOURCE_CREATE);
290         fragment.add(create);
291 
292         AjaxLink<String> edit = new IndicatingAjaxLink<>("edit") {
293 
294             private static final long serialVersionUID = 3776750333491622263L;
295 
296             @Override
297             public void onClick(final AjaxRequestTarget target) {
298                 ConnInstanceTO connInstance = connectorRestClient.read(String.class.cast(node.getKey()));
299 
300                 final IModel<ConnInstanceTO> model = new CompoundPropertyModel<>(connInstance);
301                 modal.setFormModel(model);
302 
303                 target.add(modal.setContent(new ConnectorWizardBuilder(connInstance, connectorRestClient, pageRef).
304                         build(BaseModal.CONTENT_ID,
305                                 SyncopeConsoleSession.get().
306                                         owns(IdMEntitlement.CONNECTOR_UPDATE, connInstance.getAdminRealm())
307                                 ? AjaxWizard.Mode.EDIT
308                                 : AjaxWizard.Mode.READONLY)));
309 
310                 modal.header(
311                         new Model<>(MessageFormat.format(getString("connector.edit"), connInstance.getDisplayName())));
312                 modal.show(true);
313             }
314 
315             @Override
316             public String getAjaxIndicatorMarkupId() {
317                 return Constants.VEIL_INDICATOR_MARKUP_ID;
318             }
319         };
320         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, IdMEntitlement.CONNECTOR_READ);
321         fragment.add(edit);
322 
323         AjaxLink<String> history = new IndicatingAjaxLink<>("history") {
324 
325             private static final long serialVersionUID = -1876519166660008562L;
326 
327             @Override
328             public void onClick(final AjaxRequestTarget target) {
329                 ConnInstanceTO modelObject = connectorRestClient.read(node.getKey());
330 
331                 target.add(historyModal.setContent(new AuditHistoryModal<>(
332                         AuditElements.EventCategoryType.LOGIC,
333                         "ConnectorLogic",
334                         modelObject,
335                         IdMEntitlement.CONNECTOR_UPDATE,
336                         auditRestClient) {
337 
338                     private static final long serialVersionUID = -3225348282675513648L;
339 
340                     @Override
341                     protected void restore(final String json, final AjaxRequestTarget target) {
342                         try {
343                             ConnInstanceTO updated = MAPPER.readValue(json, ConnInstanceTO.class);
344                             connectorRestClient.update(updated);
345 
346                             SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
347                             toggle(target, false);
348                         } catch (Exception e) {
349                             LOG.error("While restoring connector {}", node.getKey(), e);
350                             SyncopeConsoleSession.get().onException(e);
351                         }
352                         ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
353                     }
354                 }));
355 
356                 historyModal.header(
357                         new Model<>(MessageFormat.format(getString("connector.menu.history"), node.getDisplayName())));
358 
359                 historyModal.show(true);
360             }
361 
362             @Override
363             public String getAjaxIndicatorMarkupId() {
364                 return Constants.VEIL_INDICATOR_MARKUP_ID;
365             }
366         };
367         MetaDataRoleAuthorizationStrategy.authorize(history, RENDER,
368                 String.format("%s,%s", IdMEntitlement.CONNECTOR_READ, IdRepoEntitlement.AUDIT_LIST));
369         fragment.add(history);
370 
371         return fragment;
372     }
373 
374     private Fragment getResourceFragment(final TopologyNode node, final PageReference pageRef) {
375         Fragment fragment = new Fragment("actions", "resourceActions", this);
376 
377         AjaxLink<String> delete = new IndicatingOnConfirmAjaxLink<>("delete", Constants.CONFIRM_DELETE, true) {
378 
379             private static final long serialVersionUID = 3776750333491622263L;
380 
381             @Override
382             public void onClick(final AjaxRequestTarget target) {
383                 try {
384                     resourceRestClient.delete(node.getKey());
385                     target.appendJavaScript(String.format("jsPlumb.remove('%s');", node.getKey()));
386                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
387                     toggle(target, false);
388                 } catch (SyncopeClientException e) {
389                     LOG.error("While deleting resource {}", node.getKey(), e);
390                     SyncopeConsoleSession.get().onException(e);
391                 }
392                 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
393             }
394         };
395         MetaDataRoleAuthorizationStrategy.authorize(delete, RENDER, IdMEntitlement.RESOURCE_DELETE);
396         fragment.add(delete);
397 
398         AjaxLink<String> edit = new IndicatingAjaxLink<>("edit") {
399 
400             private static final long serialVersionUID = 3776750333491622263L;
401 
402             @Override
403             public void onClick(final AjaxRequestTarget target) {
404                 ResourceTO resource = resourceRestClient.read(node.getKey());
405                 ConnInstanceTO connInstance = connectorRestClient.read(resource.getConnector());
406 
407                 IModel<ResourceTO> model = new CompoundPropertyModel<>(resource);
408                 modal.setFormModel(model);
409 
410                 target.add(modal.setContent(new ResourceWizardBuilder(
411                         resource, resourceRestClient, connectorRestClient, pageRef).
412                         build(BaseModal.CONTENT_ID,
413                                 SyncopeConsoleSession.get().
414                                         owns(IdMEntitlement.RESOURCE_UPDATE, connInstance.getAdminRealm())
415                                 ? AjaxWizard.Mode.EDIT
416                                 : AjaxWizard.Mode.READONLY)));
417 
418                 modal.header(new Model<>(MessageFormat.format(getString("resource.edit"), node.getKey())));
419                 modal.show(true);
420             }
421 
422             @Override
423             public String getAjaxIndicatorMarkupId() {
424                 return Constants.VEIL_INDICATOR_MARKUP_ID;
425             }
426         };
427         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, IdMEntitlement.RESOURCE_READ);
428         fragment.add(edit);
429 
430         AjaxLink<String> status = new IndicatingAjaxLink<>("reconciliation") {
431 
432             private static final long serialVersionUID = 3776750333491622263L;
433 
434             @Override
435             public void onClick(final AjaxRequestTarget target) {
436                 ResourceTO modelObject = resourceRestClient.read(node.getKey());
437                 target.add(propTaskModal.setContent(new ResourceStatusModal(pageRef, modelObject)));
438                 propTaskModal.header(
439                         new Model<>(MessageFormat.format(getString("resource.reconciliation"), node.getKey())));
440                 propTaskModal.show(true);
441             }
442 
443             @Override
444             public String getAjaxIndicatorMarkupId() {
445                 return Constants.VEIL_INDICATOR_MARKUP_ID;
446             }
447         };
448         MetaDataRoleAuthorizationStrategy.authorize(status, RENDER, IdRepoEntitlement.USER_UPDATE);
449         fragment.add(status);
450 
451         AjaxLink<String> provision = new IndicatingAjaxLink<>("provision") {
452 
453             private static final long serialVersionUID = 3776750333491622263L;
454 
455             @Override
456             public void onClick(final AjaxRequestTarget target) {
457                 ResourceTO resource = resourceRestClient.read(node.getKey());
458                 ConnInstanceTO connInstance = connectorRestClient.read(resource.getConnector());
459 
460                 if (SyncopeConsoleSession.get().
461                         owns(IdMEntitlement.RESOURCE_UPDATE, connInstance.getAdminRealm())) {
462 
463                     provisionModal.addSubmitButton();
464                 } else {
465                     provisionModal.removeSubmitButton();
466                 }
467 
468                 IModel<ResourceTO> model = new CompoundPropertyModel<>(resource);
469                 provisionModal.setFormModel(model);
470 
471                 target.add(provisionModal.setContent(
472                         new ResourceProvisionPanel(provisionModal, resource, connInstance.getAdminRealm(), pageRef)));
473 
474                 provisionModal.header(new Model<>(MessageFormat.format(getString("resource.edit"), node.getKey())));
475                 provisionModal.show(true);
476             }
477 
478             @Override
479             public String getAjaxIndicatorMarkupId() {
480                 return Constants.VEIL_INDICATOR_MARKUP_ID;
481             }
482         };
483         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, IdMEntitlement.RESOURCE_READ);
484         fragment.add(provision);
485 
486         AjaxLink<String> explore = new IndicatingAjaxLink<>("explore") {
487 
488             private static final long serialVersionUID = 3776750333491622263L;
489 
490             @Override
491             public void onClick(final AjaxRequestTarget target) {
492                 ResourceTO resource = resourceRestClient.read(node.getKey());
493 
494                 target.add(propTaskModal.setContent(new ConnObjects(resource, pageRef)));
495                 propTaskModal.header(new StringResourceModel("resource.explore.list", Model.of(node)));
496                 propTaskModal.show(true);
497             }
498 
499             @Override
500             public String getAjaxIndicatorMarkupId() {
501                 return Constants.VEIL_INDICATOR_MARKUP_ID;
502             }
503         };
504         MetaDataRoleAuthorizationStrategy.authorize(explore, RENDER, IdMEntitlement.RESOURCE_LIST_CONNOBJECT);
505         fragment.add(explore);
506 
507         AjaxLink<String> propagation = new IndicatingAjaxLink<>("propagation") {
508 
509             private static final long serialVersionUID = 3776750333491622263L;
510 
511             @Override
512             @SuppressWarnings("unchecked")
513             public void onClick(final AjaxRequestTarget target) {
514                 target.add(propTaskModal.setContent(
515                         new PropagationTasks(propTaskModal, node.getKey(), pageRef)));
516                 propTaskModal.header(
517                         new Model<>(MessageFormat.format(getString("task.propagation.list"), node.getKey())));
518                 propTaskModal.show(true);
519             }
520 
521             @Override
522             public String getAjaxIndicatorMarkupId() {
523                 return Constants.VEIL_INDICATOR_MARKUP_ID;
524             }
525         };
526         MetaDataRoleAuthorizationStrategy.authorize(propagation, RENDER, IdRepoEntitlement.TASK_LIST);
527         fragment.add(propagation);
528 
529         AjaxLink<String> pull = new IndicatingAjaxLink<>("pull") {
530 
531             private static final long serialVersionUID = 3776750333491622263L;
532 
533             @Override
534             public void onClick(final AjaxRequestTarget target) {
535                 target.add(schedTaskModal.setContent(new PullTasks(schedTaskModal, node.getKey(), pageRef)));
536                 schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.pull.list"), node.getKey())));
537                 schedTaskModal.show(true);
538             }
539 
540             @Override
541             public String getAjaxIndicatorMarkupId() {
542                 return Constants.VEIL_INDICATOR_MARKUP_ID;
543             }
544         };
545         MetaDataRoleAuthorizationStrategy.authorize(pull, RENDER, IdRepoEntitlement.TASK_LIST);
546         fragment.add(pull);
547 
548         AjaxLink<String> push = new IndicatingAjaxLink<>("push") {
549 
550             private static final long serialVersionUID = 3776750333491622263L;
551 
552             @Override
553             public void onClick(final AjaxRequestTarget target) {
554                 target.add(schedTaskModal.setContent(new PushTasks(schedTaskModal, node.getKey(), pageRef)));
555                 schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.push.list"), node.getKey())));
556                 schedTaskModal.show(true);
557             }
558 
559             @Override
560             public String getAjaxIndicatorMarkupId() {
561                 return Constants.VEIL_INDICATOR_MARKUP_ID;
562             }
563         };
564         MetaDataRoleAuthorizationStrategy.authorize(push, RENDER, IdRepoEntitlement.TASK_LIST);
565         fragment.add(push);
566 
567         AjaxLink<String> history = new IndicatingAjaxLink<>("history") {
568 
569             private static final long serialVersionUID = -1876519166660008562L;
570 
571             @Override
572             public void onClick(final AjaxRequestTarget target) {
573                 ResourceTO modelObject = resourceRestClient.read(node.getKey());
574 
575                 target.add(historyModal.setContent(new AuditHistoryModal<>(
576                         AuditElements.EventCategoryType.LOGIC,
577                         "ResourceLogic",
578                         modelObject,
579                         IdMEntitlement.RESOURCE_UPDATE,
580                         auditRestClient) {
581 
582                     private static final long serialVersionUID = -3712506022627033811L;
583 
584                     @Override
585                     protected void restore(final String json, final AjaxRequestTarget target) {
586                         try {
587                             ResourceTO updated = MAPPER.readValue(json, ResourceTO.class);
588                             resourceRestClient.update(updated);
589 
590                             SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
591                             toggle(target, false);
592                         } catch (Exception e) {
593                             LOG.error("While restoring resource {}", node.getKey(), e);
594                             SyncopeConsoleSession.get().onException(e);
595                         }
596                         ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
597                     }
598                 }));
599 
600                 historyModal.header(
601                         new Model<>(MessageFormat.format(getString("resource.menu.history"), node.getDisplayName())));
602 
603                 historyModal.show(true);
604             }
605 
606             @Override
607             public String getAjaxIndicatorMarkupId() {
608                 return Constants.VEIL_INDICATOR_MARKUP_ID;
609             }
610         };
611         MetaDataRoleAuthorizationStrategy.authorize(history, RENDER,
612                 String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST));
613         fragment.add(history);
614 
615         // [SYNCOPE-1161] - Option to clone a resource
616         AjaxLink<String> clone = new IndicatingAjaxLink<>("clone") {
617 
618             private static final long serialVersionUID = -7978723352517770644L;
619 
620             @Override
621             public void onClick(final AjaxRequestTarget target) {
622                 try {
623                     ResourceTO resource = resourceRestClient.read(node.getKey());
624                     resource.setKey("Copy of " + node.getKey());
625 
626                     for (Provision provision : resource.getProvisions()) {
627                         if (provision.getMapping() != null) {
628                             provision.getMapping().getLinkingItems().clear();
629                         }
630                         provision.getVirSchemas().clear();
631                     }
632                     target.add(modal.setContent(new ResourceWizardBuilder(
633                             resource, resourceRestClient, connectorRestClient, pageRef).
634                             build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE)));
635 
636                     modal.header(new Model<>(MessageFormat.format(getString("resource.clone"), node.getKey())));
637                     modal.show(true);
638 
639                 } catch (SyncopeClientException e) {
640                     LOG.error("While cloning resource {}", node.getKey(), e);
641                     SyncopeConsoleSession.get().onException(e);
642                 }
643                 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
644             }
645 
646         };
647         MetaDataRoleAuthorizationStrategy.authorize(clone, RENDER, IdMEntitlement.RESOURCE_CREATE);
648         fragment.add(clone);
649 
650         return fragment;
651     }
652 
653     @Override
654     public void onEvent(final IEvent<?> event) {
655         super.onEvent(event);
656 
657         if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
658             AjaxWizard.NewItemFinishEvent<?> item = AjaxWizard.NewItemFinishEvent.class.cast(event.getPayload());
659             Serializable result = item.getResult();
660             Optional<AjaxRequestTarget> target = item.getTarget();
661             if (result instanceof ConnInstanceTO) {
662                 // update Toggle Panel header
663                 target.ifPresent(t -> setHeader(t, ConnInstanceTO.class.cast(result).getDisplayName()));
664             }
665         }
666     }
667 
668     public static final class UpdateEvent {
669 
670         private final AjaxRequestTarget target;
671 
672         private final String key;
673 
674         public UpdateEvent(final String key, final AjaxRequestTarget target) {
675             this.target = target;
676             this.key = key;
677         }
678 
679         public AjaxRequestTarget getTarget() {
680             return target;
681         }
682 
683         public String getKey() {
684             return key;
685         }
686     }
687 }