This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit a355b0800f124dbd53aff374e63d12b6e87efd35
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Mon Jun 28 16:26:29 2021 +0200

    [SYNCOPE-1545] WA > Sessions
---
 .../client/console/commons/AMConstants.java        |   2 +
 .../apache/syncope/client/console/pages/WA.java    |  31 +--
 .../console/panels/SRARouteDirectoryPanel.java     |   2 -
 .../console/panels/WASessionDirectoryPanel.java    | 209 +++++++++++++++++++++
 .../client/console/panels/WASessionPanel.java      |  91 +++++++++
 .../syncope/client/console/rest/WASession.java     |  71 +++++++
 .../client/console/rest/WASessionDeserializer.java |  83 ++++++++
 .../client/console/rest/WASessionRestClient.java   | 106 +++++++++++
 .../panels/WASessionDirectoryPanel.properties      |  22 +++
 .../WASessionDirectoryPanel_fr_CA.properties       |  22 +++
 .../panels/WASessionDirectoryPanel_it.properties   |  22 +++
 .../panels/WASessionDirectoryPanel_ja.properties   |  22 +++
 .../WASessionDirectoryPanel_pt_BR.properties       |  22 +++
 .../panels/WASessionDirectoryPanel_ru.properties   |  22 +++
 .../client/console/panels/WASessionPanel.html      |  36 ++++
 .../client/console/rest/BaseRestClient.java        |   4 +-
 .../syncope/common/lib/types/AMEntitlement.java    |   4 +
 17 files changed, 752 insertions(+), 19 deletions(-)

diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
index 564227c..7b2eb35 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
@@ -24,6 +24,8 @@ public final class AMConstants {
 
     public static final String PREF_WACONFIG_PAGINATOR_ROWS = 
"waconfig.paginator.rows";
 
+    public static final String PREF_WASESSION_PAGINATOR_ROWS = 
"wasession.paginator.rows";
+
     public static final String PREF_AUTHMODULE_PAGINATOR_ROWS = 
"authmodule.paginator.rows";
 
     public static final String PREF_CLIENTAPP_PAGINATOR_ROWS = 
"clientapp.paginator.rows";
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
index 30312b4..cc283b1 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
@@ -38,13 +38,12 @@ import 
org.apache.syncope.client.console.clientapps.ClientApps;
 import org.apache.syncope.client.console.panels.OIDC;
 import org.apache.syncope.client.console.panels.SAML2;
 import org.apache.syncope.client.console.panels.WAConfigDirectoryPanel;
+import org.apache.syncope.client.console.panels.WASessionPanel;
 import org.apache.syncope.client.console.rest.WAConfigRestClient;
 import org.apache.syncope.client.ui.commons.Constants;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
@@ -75,6 +74,8 @@ public class WA extends BasePage {
         body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", 
Dashboard.class));
         body.setOutputMarkupId(true);
 
+        List<NetworkService> waInstances = 
serviceOps.list(NetworkService.Type.WA);
+
         AjaxLink<?> push = new AjaxLink<>("push") {
 
             @Override
@@ -90,19 +91,20 @@ public class WA extends BasePage {
                 ((BaseWebPage) 
getPageReference().getPage()).getNotificationPanel().refresh(target);
             }
         };
-        push.setEnabled(!serviceOps.list(NetworkService.Type.WA).isEmpty()
-                && 
SyncopeConsoleSession.get().owns(AMEntitlement.WA_CONFIG_PUSH, 
SyncopeConstants.ROOT_REALM));
+        push.setEnabled(!waInstances.isEmpty()
+                && 
SyncopeConsoleSession.get().owns(AMEntitlement.WA_CONFIG_PUSH));
         body.add(push);
 
         WebMarkupContainer content = new WebMarkupContainer("content");
         content.setOutputMarkupId(true);
-        AjaxBootstrapTabbedPanel<ITab> tabbedPanel = new 
AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList());
+        AjaxBootstrapTabbedPanel<ITab> tabbedPanel =
+                new AjaxBootstrapTabbedPanel<>("tabbedPanel", 
buildTabList(waInstances));
         content.add(tabbedPanel);
 
         body.add(content);
 
-        if (!serviceOps.list(NetworkService.Type.WA).isEmpty()) {
-            String actuatorEndpoint = 
serviceOps.list(NetworkService.Type.WA).get(0).getAddress() + "/actuator/env";
+        if (!waInstances.isEmpty()) {
+            String actuatorEndpoint = waInstances.get(0).getAddress() + 
"/actuator/env";
             try {
                 Response response = WebClient.create(
                         actuatorEndpoint,
@@ -132,10 +134,10 @@ public class WA extends BasePage {
         }
     }
 
-    private List<ITab> buildTabList() {
+    private List<ITab> buildTabList(final List<NetworkService> waInstances) {
         List<ITab> tabs = new ArrayList<>(0);
 
-        if (SyncopeConsoleSession.get().owns(AMEntitlement.AUTH_MODULE_LIST, 
SyncopeConstants.ROOT_REALM)) {
+        if (SyncopeConsoleSession.get().owns(AMEntitlement.AUTH_MODULE_LIST)) {
             tabs.add(new AbstractTab(new ResourceModel("authModules")) {
 
                 private static final long serialVersionUID = 
5211692813425391144L;
@@ -147,7 +149,7 @@ public class WA extends BasePage {
             });
         }
 
-        if (SyncopeConsoleSession.get().owns(AMEntitlement.CLIENTAPP_LIST, 
SyncopeConstants.ROOT_REALM)) {
+        if (SyncopeConsoleSession.get().owns(AMEntitlement.CLIENTAPP_LIST)) {
             tabs.add(new AbstractTab(new ResourceModel("clientApps")) {
 
                 private static final long serialVersionUID = 
5211692813425391144L;
@@ -179,7 +181,7 @@ public class WA extends BasePage {
             }
         });
 
-        if (SyncopeConsoleSession.get().owns(AMEntitlement.WA_CONFIG_LIST, 
SyncopeConstants.ROOT_REALM)) {
+        if (SyncopeConsoleSession.get().owns(AMEntitlement.WA_CONFIG_LIST)) {
             tabs.add(new AbstractTab(new ResourceModel("config")) {
 
                 private static final long serialVersionUID = 
5211692813425391144L;
@@ -191,7 +193,7 @@ public class WA extends BasePage {
             });
         }
 
-        if (SyncopeConsoleSession.get().owns(AMEntitlement.AUTH_PROFILE_LIST, 
SyncopeConstants.ROOT_REALM)) {
+        if (SyncopeConsoleSession.get().owns(AMEntitlement.AUTH_PROFILE_LIST)) 
{
             tabs.add(new AbstractTab(new ResourceModel("authProfiles")) {
 
                 private static final long serialVersionUID = 
5211692813425391144L;
@@ -203,15 +205,14 @@ public class WA extends BasePage {
             });
         }
 
-        List<NetworkService> instances = 
serviceOps.list(NetworkService.Type.WA);
-        if (!instances.isEmpty()) {
+        if (!waInstances.isEmpty() && 
SyncopeConsoleSession.get().owns(AMEntitlement.WA_SESSION_LIST)) {
             tabs.add(new AbstractTab(new ResourceModel("sessions")) {
 
                 private static final long serialVersionUID = 
5211692813425391144L;
 
                 @Override
                 public Panel getPanel(final String panelId) {
-                    return new AjaxTextFieldPanel(panelId, panelId, 
Model.of(instances.get(0).getAddress()));
+                    return new WASessionPanel(panelId, waInstances, 
getPageReference());
                 }
             });
         }
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java
index 7008abe..2357cb1 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/SRARouteDirectoryPanel.java
@@ -67,8 +67,6 @@ public class SRARouteDirectoryPanel
             modal.show(false);
         });
 
-        restClient = new SRARouteRestClient();
-
         addNewItemPanelBuilder(new SRARouteWizardBuilder(new SRARouteTO(), 
pageRef), true);
         initResultTable();
     }
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WASessionDirectoryPanel.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WASessionDirectoryPanel.java
new file mode 100644
index 0000000..db74094
--- /dev/null
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WASessionDirectoryPanel.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.AMConstants;
+import 
org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import 
org.apache.syncope.client.console.panels.WASessionDirectoryPanel.WASessionProvider;
+import 
org.apache.syncope.client.console.panels.WASessionPanel.WASessionSearchEvent;
+import org.apache.syncope.client.console.rest.WASession;
+import org.apache.syncope.client.console.rest.WASessionRestClient;
+import 
org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import 
org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import 
org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import 
org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class WASessionDirectoryPanel
+        extends DirectoryPanel<WASession, WASession, WASessionProvider, 
WASessionRestClient> {
+
+    private static final long serialVersionUID = 24083331272114L;
+
+    private final BaseModal<String> viewModal;
+
+    private final List<NetworkService> waInstances;
+
+    private String keyword;
+
+    public WASessionDirectoryPanel(
+            final String id,
+            final List<NetworkService> waInstances,
+            final PageReference pageRef) {
+
+        super(id, pageRef);
+        this.waInstances = waInstances;
+
+        disableCheckBoxes();
+
+        viewModal = new BaseModal<>(Constants.OUTER) {
+
+            private static final long serialVersionUID = 389935548143327858L;
+
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setFooterVisible(false);
+            }
+        };
+        viewModal.size(Modal.Size.Extra_large);
+        viewModal.setWindowClosedCallback(target -> viewModal.show(false));
+        addOuterObject(viewModal);
+
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<WASession, String>> getColumns() {
+        List<IColumn<WASession, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<>(
+                new StringResourceModel(Constants.KEY_FIELD_NAME, this), 
Constants.KEY_FIELD_NAME));
+        columns.add(new DatePropertyColumn<>(
+                new ResourceModel("authenticationDate"), "authenticationDate", 
"authenticationDate"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("authenticatedPrincipal"), 
"authenticatedPrincipal", "authenticatedPrincipal"));
+        return columns;
+    }
+
+    @Override
+    protected ActionsPanel<WASession> getActions(final IModel<WASession> 
model) {
+        ActionsPanel<WASession> panel = super.getActions(model);
+
+        panel.add(new ActionLink<WASession>() {
+
+            private static final long serialVersionUID = 22687128346032L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final 
WASession ignore) {
+                viewModal.header(new ResourceModel("details"));
+                target.add(viewModal.setContent(
+                        new JsonEditorPanel(viewModal, 
Model.of(model.getObject().getJson()), true, pageRef)));
+                viewModal.show(true);
+            }
+        }, ActionLink.ActionType.VIEW, AMEntitlement.WA_SESSION_LIST);
+
+        panel.add(new ActionLink<WASession>() {
+
+            private static final long serialVersionUID = -4608353559809323466L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final 
WASession ignore) {
+                WASession waSession = model.getObject();
+                try {
+                    WASessionRestClient.delete(waInstances, 
waSession.getKey());
+                    
SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", waSession.getKey(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BaseWebPage) 
pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, AMEntitlement.WA_SESSION_DELETE, 
true);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return List.of();
+    }
+
+    @Override
+    protected WASessionProvider dataProvider() {
+        return new WASessionProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return AMConstants.PREF_WASESSION_PAGINATOR_ROWS;
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof WASessionSearchEvent) {
+            WASessionSearchEvent payload = 
WASessionSearchEvent.class.cast(event.getPayload());
+            keyword = payload.getKeyword();
+
+            updateResultTable(payload.getTarget());
+        } else {
+            super.onEvent(event);
+        }
+    }
+
+    protected final class WASessionProvider extends 
DirectoryDataProvider<WASession> {
+
+        private static final long serialVersionUID = 18002870965042L;
+
+        private final SortableDataProviderComparator<WASession> comparator;
+
+        public WASessionProvider(final int paginatorRows) {
+            super(paginatorRows);
+            setSort("authenticationDate", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        private Stream<WASession> filtered() {
+            Stream<WASession> stream = 
WASessionRestClient.list(waInstances).stream();
+            return keyword == null
+                    ? stream
+                    : stream.filter(s -> s.getJson().contains(keyword));
+        }
+
+        @Override
+        public Iterator<? extends WASession> iterator(final long first, final 
long count) {
+            return 
filtered().skip(first).limit(count).sorted(comparator).iterator();
+        }
+
+        @Override
+        public long size() {
+            return filtered().count();
+        }
+
+        @Override
+        public IModel<WASession> model(final WASession waSession) {
+            return new CompoundPropertyModel<>(waSession);
+        }
+    }
+}
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WASessionPanel.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WASessionPanel.java
new file mode 100644
index 0000000..24439d9
--- /dev/null
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/WASessionPanel.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.io.Serializable;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+
+public class WASessionPanel extends Panel {
+
+    private static final long serialVersionUID = 30189416462011L;
+
+    public WASessionPanel(final String id, final List<NetworkService> 
waInstances, final PageReference pageRef) {
+        super(id);
+
+        Model<String> keywordModel = new Model<>(StringUtils.EMPTY);
+
+        WebMarkupContainer searchBoxContainer = new 
WebMarkupContainer("searchBox");
+        add(searchBoxContainer);
+
+        WASessionDirectoryPanel directoryPanel = new 
WASessionDirectoryPanel("directoryPanel", waInstances, pageRef);
+        add(directoryPanel);
+
+        Form<?> form = new Form<>("form");
+        searchBoxContainer.add(form);
+
+        AjaxTextFieldPanel filter = new AjaxTextFieldPanel("filter", "filter", 
keywordModel, true);
+        
form.add(filter.hideLabel().setOutputMarkupId(true).setRenderBodyOnly(true));
+
+        AjaxButton search = new AjaxButton("search") {
+
+            private static final long serialVersionUID = 8390605330558248736L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target) {
+                send(directoryPanel, Broadcast.EXACT, new 
WASessionSearchEvent(target, keywordModel.getObject()));
+            }
+        };
+        search.setOutputMarkupId(true);
+        form.add(search);
+        form.setDefaultButton(search);
+    }
+
+    public static class WASessionSearchEvent implements Serializable {
+
+        private static final long serialVersionUID = 5063826346823013424L;
+
+        private final transient AjaxRequestTarget target;
+
+        private final String keyword;
+
+        WASessionSearchEvent(final AjaxRequestTarget target, final String 
keyword) {
+            this.target = target;
+            this.keyword = keyword;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public String getKeyword() {
+            return keyword;
+        }
+    }
+}
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASession.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASession.java
new file mode 100644
index 0000000..2ecdda2
--- /dev/null
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASession.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.rest;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import java.util.Date;
+import org.apache.syncope.common.lib.to.EntityTO;
+
+@JsonDeserialize(using = WASessionDeserializer.class)
+public class WASession implements EntityTO {
+
+    private static final long serialVersionUID = 18201657700802L;
+
+    private String key;
+
+    private Date authenticationDate;
+
+    private String authenticatedPrincipal;
+
+    private String json;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public Date getAuthenticationDate() {
+        return authenticationDate;
+    }
+
+    public void setAuthenticationDate(final Date authenticationDate) {
+        this.authenticationDate = authenticationDate;
+    }
+
+    public String getAuthenticatedPrincipal() {
+        return authenticatedPrincipal;
+    }
+
+    public void setAuthenticatedPrincipal(final String authenticatedPrincipal) 
{
+        this.authenticatedPrincipal = authenticatedPrincipal;
+    }
+
+    public String getJson() {
+        return json;
+    }
+
+    public void setJson(final String json) {
+        this.json = json;
+    }
+}
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionDeserializer.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionDeserializer.java
new file mode 100644
index 0000000..12d39d9
--- /dev/null
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionDeserializer.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.rest;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.ParseException;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WASessionDeserializer extends StdDeserializer<WASession> {
+
+    private static final long serialVersionUID = 24527200564172L;
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(WASessionDeserializer.class);
+
+    public WASessionDeserializer() {
+        this(null);
+    }
+
+    public WASessionDeserializer(final Class<?> vc) {
+        super(vc);
+    }
+
+    @Override
+    public WASession deserialize(final JsonParser jp, final 
DeserializationContext ctxt)
+            throws IOException, JsonProcessingException {
+
+        JsonNode node = jp.getCodec().readTree(jp);
+
+        WASession waSession = new WASession();
+
+        if (node.has("authentication_date_formatted")) {
+            String authenticationDate = 
node.get("authentication_date_formatted").textValue();
+            try {
+                waSession.setAuthenticationDate(
+                        
DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.parse(authenticationDate));
+            } catch (ParseException e) {
+                LOG.error("Unparsable date: {}", authenticationDate, e);
+            }
+        }
+
+        if (node.has("authenticated_principal")) {
+            
waSession.setAuthenticatedPrincipal(node.get("authenticated_principal").textValue());
+        }
+
+        if (node.has("ticket_granting_ticket")) {
+            waSession.setKey(node.get("ticket_granting_ticket").textValue());
+        }
+
+        StringWriter writer = new StringWriter();
+        JsonGenerator jgen = 
jp.getCodec().getFactory().createGenerator(writer);
+        jgen.setPrettyPrinter(new DefaultPrettyPrinter());
+        jp.getCodec().writeTree(jgen, node);
+        waSession.setJson(writer.toString());
+
+        return waSession;
+    }
+}
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java
new file mode 100644
index 0000000..94aaf65
--- /dev/null
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/WASessionRestClient.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.rest;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.InputStream;
+import java.util.List;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.console.SyncopeWebApplication;
+import org.apache.syncope.client.ui.commons.rest.RestClient;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class WASessionRestClient implements RestClient {
+
+    private static final long serialVersionUID = 22118820292494L;
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(WASessionRestClient.class);
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private static String getActuatorEndpoint(final List<NetworkService> 
waInstances) {
+        return waInstances.get(0).getAddress() + "/actuator/ssoSessions";
+    }
+
+    public static List<WASession> list(final List<NetworkService> waInstances) 
{
+        try {
+            Response response = WebClient.create(
+                    getActuatorEndpoint(waInstances),
+                    SyncopeWebApplication.get().getAnonymousUser(),
+                    SyncopeWebApplication.get().getAnonymousKey(),
+                    null).
+                    accept(MediaType.APPLICATION_JSON_TYPE).get();
+            if (response.getStatus() == Response.Status.OK.getStatusCode()) {
+                JsonNode node = MAPPER.readTree((InputStream) 
response.getEntity());
+                if (node.has("activeSsoSessions")) {
+                    return MAPPER.readValue(
+                            MAPPER.treeAsTokens(node.get("activeSsoSessions")),
+                            new TypeReference<List<WASession>>() {
+                    });
+                }
+            } else {
+                LOG.error("Unexpected response for SSO Sessions from {}: {}",
+                        getActuatorEndpoint(waInstances), 
response.getStatus());
+            }
+        } catch (Exception e) {
+            LOG.error("Could not fetch SSO Sessions from {}", 
getActuatorEndpoint(waInstances), e);
+        }
+
+        return List.of();
+    }
+
+    public static void delete(final List<NetworkService> waInstances, final 
String ticketGrantingTicket) {
+        SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.Unknown);
+
+        try {
+            Response response = WebClient.create(
+                    getActuatorEndpoint(waInstances),
+                    SyncopeWebApplication.get().getAnonymousUser(),
+                    SyncopeWebApplication.get().getAnonymousKey(),
+                    null).
+                    path(ticketGrantingTicket).
+                    
accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE).delete();
+            if (response.getStatus() != Response.Status.OK.getStatusCode()) {
+                LOG.error("Unexpected response when deleting SSO Session {} 
from {}: {}",
+                        ticketGrantingTicket, 
getActuatorEndpoint(waInstances), response.getStatus());
+                sce.getElements().add("Unexpected response code: " + 
response.getStatus());
+            }
+        } catch (Exception e) {
+            LOG.error("Could not delete SSO Session {} from {}",
+                    ticketGrantingTicket, getActuatorEndpoint(waInstances), e);
+            sce.getElements().add("Unexpected error: " + e.getMessage());
+        }
+
+        if (!sce.getElements().isEmpty()) {
+            throw sce;
+        }
+    }
+
+    private WASessionRestClient() {
+        // private constructor for static utility class
+    }
+}
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel.properties
new file mode 100644
index 0000000..7883872
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel.properties
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+any.edit=Edit ${key}
+any.finish=Submit ${key}
+any.cancel=Cancel ${key}
+authenticationDate=Creation Date
+authenticatedPrincipal=Principal
+details=Details
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_fr_CA.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_fr_CA.properties
new file mode 100644
index 0000000..0baec19
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_fr_CA.properties
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+any.edit=Modifier ${key}
+any.finish=Soumettre ${key}
+any.cancel=Annuler ${key}
+authenticationDate=Creation Date
+authenticatedPrincipal=Principal
+details=Details
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_it.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_it.properties
new file mode 100644
index 0000000..6df8cdc
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_it.properties
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+any.edit=Modifica ${key}
+any.finish=Invia ${key}
+any.cancel=Annulla ${key}
+authenticationDate=Data di Creazione
+authenticatedPrincipal=Soggetto
+details=Dettagli
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_ja.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_ja.properties
new file mode 100644
index 0000000..c5c1d0d
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_ja.properties
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+any.edit=${key} \u3092\u7de8\u96c6
+any.finish=${key} \u3092\u5b9f\u884c
+any.cancel=${key} \u3092\u30ad\u30e3\u30f3\u30bb\u30eb
+authenticationDate=Creation Date
+authenticatedPrincipal=Principal
+details=Details
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_pt_BR.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..7883872
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_pt_BR.properties
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+any.edit=Edit ${key}
+any.finish=Submit ${key}
+any.cancel=Cancel ${key}
+authenticationDate=Creation Date
+authenticatedPrincipal=Principal
+details=Details
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_ru.properties
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_ru.properties
new file mode 100644
index 0000000..63e4a85
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionDirectoryPanel_ru.properties
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c ${key}
+any.finish=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c ${key}
+any.cancel=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c ${key}
+authenticationDate=Creation Date
+authenticatedPrincipal=Principal
+details=Details
diff --git 
a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionPanel.html
 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionPanel.html
new file mode 100644
index 0000000..362e7ed
--- /dev/null
+++ 
b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/WASessionPanel.html
@@ -0,0 +1,36 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"; 
xmlns:wicket="http://wicket.apache.org";>
+  <wicket:panel>
+    <div wicket:id="searchBox">
+      <form wicket:id="form">
+        <div class="input-group mb-3">
+          <span wicket:id="filter">[FILTER]</span>
+          <span class="input-group-btn">
+            <button type="button" class="btn btn-default btn-flat" 
wicket:id="search">
+              <span class="fas fa-search" aria-hidden="true"></span>
+            </button>
+          </span>
+        </div>
+      </form>
+    </div>
+
+    <div wicket:id="directoryPanel"></div>
+  </wicket:panel>
+</html>
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/BaseRestClient.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/BaseRestClient.java
index 8127725..979d173 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/BaseRestClient.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/BaseRestClient.java
@@ -38,10 +38,10 @@ import org.slf4j.LoggerFactory;
 
 public abstract class BaseRestClient implements RestClient {
 
-    protected static final Logger LOG = 
LoggerFactory.getLogger(BaseRestClient.class);
-
     private static final long serialVersionUID = 1523999867826481989L;
 
+    protected static final Logger LOG = 
LoggerFactory.getLogger(BaseRestClient.class);
+
     public static SyncopeService getSyncopeService() {
         return getService(SyncopeService.class);
     }
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index 7de374e..1869e18 100644
--- 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -96,6 +96,10 @@ public final class AMEntitlement {
 
     public static final String WA_CONFIG_PUSH = "WA_CONFIG_PUSH";
 
+    public static final String WA_SESSION_LIST = "WA_SESSION_LIST";
+
+    public static final String WA_SESSION_DELETE = "WA_SESSION_DELETE";
+
     private static final Set<String> VALUES;
 
     static {

Reply via email to