From 2c43d2970c8ba74d3dd85cd6916042cea708573b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 7 Nov 2025 11:12:35 +0100 Subject: [PATCH 1/5] [SYNCOPE-1928] Implementing self-refresh behavior via WebSocket --- .../panels/AMSessionDirectoryPanel.java | 5 +- .../panels/LinkedAccountModalPanel.java | 4 +- .../panels/RemediationDirectoryPanel.java | 3 +- .../ProvisioningPolicyModalPanel.java | 27 +++-- .../status/AnyStatusDirectoryPanel.java | 6 +- .../client/console/topology/Topology.java | 27 ++--- .../console/topology/TopologyTogglePanel.java | 6 +- .../console/widgets/RemediationsWidget.java | 73 +----------- .../any/LinkedAccountWizardBuilder.java | 3 +- .../any/MergeLinkedAccountsSearchPanel.java | 11 +- .../syncope/client/ui/commons/BaseLogin.java | 10 -- .../SyncopeUIRequestCycleListener.java | 44 +++----- .../commons/wizards/any/AbstractGroups.java | 9 +- client/idrepo/console/pom.xml | 18 ++- .../client/console/IdRepoConsoleContext.java | 37 +++++++ ...avior.java => SessionTimeoutListener.java} | 26 ++--- .../console/SyncopeConsoleApplication.java | 15 ++- .../client/console/SyncopeConsoleSession.java | 21 ++-- .../client/console/SyncopeWebApplication.java | 57 +++++++++- .../client/console/batch/BatchContent.java | 49 +++------ .../ClassPathScanImplementationLookup.java | 4 +- .../client/console/pages/BasePage.java | 10 +- .../syncope/client/console/pages/Realms.java | 9 +- .../client/console/panels/AnyPanel.java | 16 ++- .../console/panels/CommandDirectoryPanel.java | 4 +- .../panels/DashboardOverviewPanel.java | 30 +++-- .../client/console/panels/LogsPanel.java | 5 +- .../console/panels/SchemaTypePanel.java | 4 +- .../console/reports/ReportDirectoryPanel.java | 17 ++- .../tasks/ProvisioningTaskDirectoryPanel.java | 13 +-- .../tasks/SchedTaskDirectoryPanel.java | 13 +-- .../console/tasks/TaskDirectoryPanel.java | 5 +- .../html/form/ActionLinksTogglePanel.java | 4 +- .../wicket/ws/RefreshWebSocketBehavior.java | 77 +++++++++++++ .../client/console/widgets/AlertWidget.java | 29 +---- .../console/widgets/ExtAlertWidget.java | 22 +++- .../widgets/ItemTransformerWidget.java | 18 +-- .../widgets/JEXLTransformerWidget.java | 35 ++---- .../client/console/widgets/JobWidget.java | 15 ++- .../wizards/UserSelectionWizardStep.java | 42 +++---- .../console/wizards/WizardMgtPanel.java | 5 +- .../any/AnyObjectTemplateWizardBuilder.java | 5 +- .../console/wizards/any/AnyWizardBuilder.java | 17 ++- .../client/console/wizards/any/Details.java | 8 +- .../any/GroupTemplateWizardBuilder.java | 5 +- .../wizards/any/GroupWizardBuilder.java | 4 +- .../client/console/wizards/any/Ownership.java | 6 +- .../console/wizards/any/PlainAttrs.java | 22 ++-- .../console/wizards/any/Relationships.java | 23 ++-- .../any/UserTemplateWizardBuilder.java | 5 +- .../wizards/any/UserWizardBuilder.java | 4 +- .../syncope/client/console/pages/Login.html | 1 - .../client/console/widgets/AlertWidget.html | 1 - .../syncope/client/console/AbstractTest.java | 16 ++- .../SyncopeEnduserRequestCycleListener.java | 104 ------------------ .../client/enduser/SyncopeWebApplication.java | 7 +- .../pages/SelfConfirmPasswordReset.java | 4 +- .../enduser/panels/UserSelfFormPanel.java | 4 +- .../client/enduser/panels/any/Groups.java | 9 +- .../client/enduser/panels/any/Resources.java | 32 +++--- .../syncope/client/enduser/pages/Login.html | 1 - .../api/data/AccessTokenDataBinder.java | 3 - .../java/data/AccessTokenDataBinderImpl.java | 5 +- .../panels/UserRequestDirectoryPanel.java | 3 +- .../panels/UserRequestFormDirectoryPanel.java | 7 +- .../widgets/UserRequestFormsWidget.java | 81 +++----------- .../client/console/pages/OIDCClientLogin.java | 16 ++- .../console/pages/OIDCClientSelfReg.java | 9 +- .../panels/OIDCProvidersDirectoryPanel.java | 3 +- .../client/enduser/pages/OIDCClientLogin.java | 16 ++- .../client/console/pages/SAML2SPLogin.java | 16 ++- .../client/console/pages/SAML2SPSelfReg.java | 11 +- .../panels/SAML2IdPsDirectoryPanel.java | 3 +- .../client/enduser/pages/SAML2SPLogin.java | 16 ++- fit/core-reference/pom.xml | 5 + .../fit/console/AbstractConsoleITCase.java | 13 +++ 76 files changed, 605 insertions(+), 708 deletions(-) rename client/idrepo/console/src/main/java/org/apache/syncope/client/console/{wicket/ajax/IndicatorAjaxTimerBehavior.java => SessionTimeoutListener.java} (55%) create mode 100644 client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ws/RefreshWebSocketBehavior.java delete mode 100644 client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserRequestCycleListener.java diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java index 852f53bf9d3..88d118df6a8 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AMSessionDirectoryPanel.java @@ -123,7 +123,7 @@ protected ActionsPanel getActions(final IModel model) { public void onClick(final AjaxRequestTarget target, final AMSession ignore) { viewModal.header(new ResourceModel("details")); target.add(viewModal.setContent( - new JsonEditorPanel(viewModal, Model.of(model.getObject().getJson()), true, pageRef))); + new JsonEditorPanel(viewModal, Model.of(model.getObject().getJson()), true, pageRef))); viewModal.show(true); } }, ActionLink.ActionType.VIEW, listEntitlement); @@ -167,8 +167,7 @@ protected String paginatorRowsKey() { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof AMSessionSearchEvent) { - AMSessionSearchEvent payload = AMSessionSearchEvent.class.cast(event.getPayload()); + if (event.getPayload() instanceof AMSessionSearchEvent payload) { keyword = payload.getKeyword(); updateResultTable(payload.getTarget()); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java index 9cd543ca023..84b29902834 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java @@ -114,8 +114,8 @@ public LinkedAccountModalPanel( roleRestClient, anyTypeRestClient.listAnyTypes().stream().map(AnyTypeTO::getKey).collect(Collectors.toList())); LinkedAccountFormLayoutInfo linkedAccountFormLayoutInfo = - anyLayout.getUser() instanceof IdMUserFormLayoutInfo - ? IdMUserFormLayoutInfo.class.cast(anyLayout.getUser()).getLinkedAccountFormLayoutInfo() + anyLayout.getUser() instanceof IdMUserFormLayoutInfo idMUFLI + ? idMUFLI.getLinkedAccountFormLayoutInfo() : new LinkedAccountFormLayoutInfo(); try { diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java index 3d937cb5dd8..f459a8aaa44 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java @@ -345,8 +345,7 @@ public RemediationProvider(final int paginatorRows) { @Override public Iterator iterator(final long first, final long count) { int page = ((int) first / paginatorRows); - return restClient.getRemediations((page < 0 ? 0 : page) + 1, - paginatorRows, getSort()).iterator(); + return restClient.getRemediations((page < 0 ? 0 : page) + 1, paginatorRows, getSort()).iterator(); } @Override diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java index c4f3fe0faca..9b4915eebea 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java @@ -216,20 +216,23 @@ protected class CorrelationRulePanel extends Panel { @Override public Boolean getObject() { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - return conf instanceof DefaultInboundCorrelationRuleConf - ? DefaultInboundCorrelationRuleConf.class.cast(conf).isOrSchemas() - : conf instanceof DefaultPushCorrelationRuleConf - ? DefaultPushCorrelationRuleConf.class.cast(conf).isOrSchemas() + return conf instanceof DefaultInboundCorrelationRuleConf dicrc + ? dicrc.isOrSchemas() + : conf instanceof DefaultPushCorrelationRuleConf dpcrc + ? dpcrc.isOrSchemas() : false; } @Override public void setObject(final Boolean object) { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - if (conf instanceof DefaultInboundCorrelationRuleConf) { - DefaultInboundCorrelationRuleConf.class.cast(conf).setOrSchemas(object); - } else if (conf instanceof DefaultPushCorrelationRuleConf) { - DefaultPushCorrelationRuleConf.class.cast(conf).setOrSchemas(object); + switch (conf) { + case DefaultInboundCorrelationRuleConf dicrc -> + dicrc.setOrSchemas(object); + case DefaultPushCorrelationRuleConf dpcrc -> + dpcrc.setOrSchemas(object); + default -> { + } } } }; @@ -244,10 +247,10 @@ public void setObject(final Boolean object) { private List schemas() { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - return conf instanceof DefaultInboundCorrelationRuleConf - ? DefaultInboundCorrelationRuleConf.class.cast(conf).getSchemas() - : conf instanceof DefaultPushCorrelationRuleConf - ? DefaultPushCorrelationRuleConf.class.cast(conf).getSchemas() + return conf instanceof DefaultInboundCorrelationRuleConf dicrc + ? dicrc.getSchemas() + : conf instanceof DefaultPushCorrelationRuleConf dpcrc + ? dpcrc.getSchemas() : List.of(); } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java index 18b42ed9455..563c90358c5 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/AnyStatusDirectoryPanel.java @@ -257,9 +257,7 @@ public void onClick(final AjaxRequestTarget target, final StatusBean bean) { }, ActionLink.ActionType.RECONCILIATION_PULL, IdRepoEntitlement.TASK_EXECUTE); } - if (anyTO instanceof UserTO && !UserTO.class.cast(anyTO).getLinkedAccounts().isEmpty()) { - UserTO userTO = UserTO.class.cast(anyTO); - + if (anyTO instanceof UserTO userTO && userTO.getLinkedAccounts().isEmpty()) { if (!userTO.getLinkedAccounts().isEmpty() && userTO.getLinkedAccounts().stream().anyMatch(linkedAccountTO -> { return linkedAccountTO.getResource().equals(model.getObject().getResource()); @@ -272,7 +270,7 @@ public void onClick(final AjaxRequestTarget target, final StatusBean bean) { @Override public void onClick(final AjaxRequestTarget target, final StatusBean bean) { multiLevelPanelRef.next("ACCOUNTS", - new LinkedAccountsStatusModalPanel(Model.of(UserTO.class.cast(anyTO)), pageRef), + new LinkedAccountsStatusModalPanel(Model.of(userTO), pageRef), target); target.add(multiLevelPanelRef); AnyStatusDirectoryPanel.this.getTogglePanel().close(target); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java index cbc218e1dee..a7cbcbf7593 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java @@ -240,7 +240,7 @@ protected void populateItem(final ListItem item) { topologynode.setX(x); topologynode.setY(y); - servers.put(String.class.cast(topologynode.getKey()), topologynode); + servers.put(topologynode.getKey(), topologynode); item.add(topologyNodePanel("cs", topologynode, false)); @@ -279,7 +279,7 @@ protected void populateItem(final ListItem item) { topologynode.setX(x); topologynode.setY(y); - servers.put(String.class.cast(topologynode.getKey()), topologynode); + servers.put(topologynode.getKey(), topologynode); item.add(topologyNodePanel("fp", topologynode, false)); @@ -336,7 +336,7 @@ protected void populateItem(final ListItem item) { topologynode.setX(x); topologynode.setY(y); - connectors.put(String.class.cast(topologynode.getKey()), topologynode); + connectors.put(topologynode.getKey(), topologynode); item.add(topologyNodePanel("conn", topologynode, conn.isErrored())); // Update connections @@ -560,29 +560,26 @@ protected void onEvent(final AjaxRequestTarget target) { } @Override - @SuppressWarnings("unchecked") public void onEvent(final IEvent event) { super.onEvent(event); - if (event.getPayload() instanceof CreateEvent) { - CreateEvent resourceCreateEvent = CreateEvent.class.cast(event.getPayload()); - + if (event.getPayload() instanceof CreateEvent payload) { TopologyNode node = new TopologyNode( - resourceCreateEvent.getKey(), - resourceCreateEvent.getDisplayName(), - resourceCreateEvent.getKind()); + payload.getKey(), + payload.getDisplayName(), + payload.getKind()); newlyCreated.getModelObject().add(node); - resourceCreateEvent.getTarget().add(newlyCreatedContainer); + payload.getTarget().add(newlyCreatedContainer); - resourceCreateEvent.getTarget().appendJavaScript(String.format( + payload.getTarget().appendJavaScript(String.format( "window.Wicket.WebSocket.send('" + "{\"kind\":\"%s\",\"target\":\"%s\",\"source\":\"%s\",\"scope\":\"%s\"}" + "');", SupportedOperation.ADD_ENDPOINT, - resourceCreateEvent.getKey(), - resourceCreateEvent.getParent(), - resourceCreateEvent.getKind())); + payload.getKey(), + payload.getParent(), + payload.getKind())); } } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java index 22d07abea0d..1a775d6994d 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java @@ -239,7 +239,7 @@ private Fragment getConnectorFragment(final TopologyNode node, final PageReferen @Override public void onClick(final AjaxRequestTarget target) { try { - connectorRestClient.delete(String.class.cast(node.getKey())); + connectorRestClient.delete(node.getKey()); target.appendJavaScript(String.format("jsPlumb.remove('%s');", node.getKey())); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); toggle(target, false); @@ -260,7 +260,7 @@ public void onClick(final AjaxRequestTarget target) { @Override public void onClick(final AjaxRequestTarget target) { final ResourceTO modelObject = new ResourceTO(); - modelObject.setConnector(String.class.cast(node.getKey())); + modelObject.setConnector(node.getKey()); modelObject.setConnectorDisplayName(node.getDisplayName()); final IModel model = new CompoundPropertyModel<>(modelObject); @@ -289,7 +289,7 @@ public String getAjaxIndicatorMarkupId() { @Override public void onClick(final AjaxRequestTarget target) { - ConnInstanceTO connInstance = connectorRestClient.read(String.class.cast(node.getKey())); + ConnInstanceTO connInstance = connectorRestClient.read(node.getKey()); final IModel model = new CompoundPropertyModel<>(connInstance); modal.setFormModel(model); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java index 3fac6f481b3..5bd8c174166 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java @@ -20,102 +20,37 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; import org.apache.syncope.client.console.BookmarkablePageLinkBuilder; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.pages.Remediations; import org.apache.syncope.client.console.rest.RemediationRestClient; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.ui.commons.annotations.ExtWidget; -import org.apache.syncope.common.lib.to.RemediationTO; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.wicket.PageReference; -import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; -import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.link.AbstractLink; import org.apache.wicket.markup.html.link.BookmarkablePageLink; -import org.apache.wicket.model.IModel; -import org.apache.wicket.model.util.ListModel; import org.apache.wicket.spring.injection.annot.SpringBean; @ExtWidget(priority = 0) -public class RemediationsWidget extends ExtAlertWidget { +public class RemediationsWidget extends ExtAlertWidget { private static final long serialVersionUID = 1817429725840355068L; @SpringBean protected RemediationRestClient remediationRestClient; - protected final List lastRemediations = new ArrayList<>(); + protected final boolean authorized; public RemediationsWidget(final String id, final PageReference pageRef) { super(id, pageRef); - setOutputMarkupId(true); - - latestAlertsList.add(new IndicatorAjaxTimerBehavior(Duration.of(30, ChronoUnit.SECONDS)) { - - private static final long serialVersionUID = 7298597675929755960L; - - @Override - protected void onTimer(final AjaxRequestTarget target) { - if (!latestAlerts.getObject().equals(lastRemediations)) { - refreshLatestAlerts(target); - } - } - }); - } - - public final void refreshLatestAlerts(final AjaxRequestTarget target) { - latestAlerts.getObject().clear(); - latestAlerts.getObject().addAll(lastRemediations); - - long latestAlertSize = getLatestAlertsSize(); - linkAlertsNumber.setDefaultModelObject(latestAlertSize); - target.add(linkAlertsNumber); - - headerAlertsNumber.setDefaultModelObject(latestAlertSize); - target.add(headerAlertsNumber); - - target.add(latestAlertsList); - - lastRemediations.clear(); - lastRemediations.addAll(latestAlerts.getObject()); + authorized = SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_LIST); } @Override protected long getLatestAlertsSize() { - return SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_LIST) - && SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_READ) - ? remediationRestClient.countRemediations() - : 0L; - } - - @Override - protected IModel> getLatestAlerts() { - return new ListModel<>() { - - private static final long serialVersionUID = 541491929575585613L; - - @Override - public List getObject() { - List updatedRemediations; - if (SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_LIST) - && SyncopeConsoleSession.get().owns(IdMEntitlement.REMEDIATION_READ)) { - - updatedRemediations = remediationRestClient.getRemediations( - 1, MAX_SIZE, new SortParam<>("instant", true)); - } else { - updatedRemediations = List.of(); - } - - return updatedRemediations; - } - }; + return authorized ? remediationRestClient.countRemediations() : 0L; } @Override diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java index 22fcd75b055..ebf2c51c1f9 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountWizardBuilder.java @@ -118,9 +118,8 @@ private void fixPlainAttrs(final LinkedAccountTO linkedAccountTO) { @Override protected Serializable getCreateCustomPayloadEvent(final Serializable afterObject, final AjaxRequestTarget target) { - LinkedAccountTO linkedAccountTO = LinkedAccountTO.class.cast(afterObject); return new CreateEvent( - linkedAccountTO.getConnObjectKeyValue(), + LinkedAccountTO.class.cast(afterObject).getConnObjectKeyValue(), model.getObject(), target); } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java index 96cc365bece..c788b115fc1 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java @@ -33,7 +33,6 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.wicket.Component; import org.apache.wicket.PageReference; -import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.event.IEvent; import org.apache.wicket.extensions.wizard.WizardStep; import org.apache.wicket.markup.html.WebMarkupContainer; @@ -97,16 +96,12 @@ public MergeLinkedAccountsSearchPanel( @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof SearchClausePanel.SearchEvent) { - AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof SearchClausePanel.SearchEvent payload) { String fiql = "username!~" + wizardModel.getBaseUser().getUsername() + ';' + SearchUtils.buildFIQL(userSearchPanel.getModel().getObject(), SyncopeClient.getUserSearchConditionBuilder()); - userDirectoryPanel.search(fiql, target); - } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection itemSelection) { - AnySelectionDirectoryPanel.ItemSelection payload = - itemSelection; - + userDirectoryPanel.search(fiql, payload.getTarget()); + } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection payload) { AnyTO sel = payload.getSelection(); wizardModel.setMergingUser(userRestClient.read(sel.getKey())); diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java index e28ef9c2a14..74aab1280d6 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseLogin.java @@ -37,7 +37,6 @@ import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.OnLoadHeaderItem; import org.apache.wicket.markup.html.WebPage; -import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.PasswordTextField; @@ -108,15 +107,6 @@ public BaseLogin(final PageParameters parameters) { : parameters.get(Constants.NOTIFICATION_LEVEL_PARAM).toString(); } - Label exceptionMessage = new Label("exceptionMessage"); - exceptionMessage.setOutputMarkupPlaceholderTag(true); - exceptionMessage.setVisible(false); - if (!parameters.get("errorMessage").isNull()) { - exceptionMessage.setVisible(true); - exceptionMessage.setDefaultModel(Model.of(parameters.get("errorMessage"))); - } - add(exceptionMessage); - form = new StatelessForm<>("login"); usernameField = new TextField<>("username", new Model<>()); diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/SyncopeUIRequestCycleListener.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/SyncopeUIRequestCycleListener.java index 8cdbb710c5e..f330576aa9f 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/SyncopeUIRequestCycleListener.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/SyncopeUIRequestCycleListener.java @@ -32,12 +32,12 @@ import org.apache.wicket.markup.html.pages.ExceptionErrorPage; import org.apache.wicket.protocol.http.PageExpiredException; import org.apache.wicket.request.IRequestHandler; -import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.cycle.IRequestCycleListener; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.wicketstuff.kendo.ui.widget.notification.Notification; public abstract class SyncopeUIRequestCycleListener implements IRequestCycleListener { @@ -66,61 +66,53 @@ private static Optional instanceOf(final Exception e, f public IRequestHandler onException(final RequestCycle cycle, final Exception e) { LOG.error("Exception found", e); - PageParameters errorParameters = new PageParameters(); + PageParameters parameters = new PageParameters(); + parameters.add(Constants.NOTIFICATION_LEVEL_PARAM, Notification.ERROR); - IRequestablePage errorPage; if (instanceOf(e, UnauthorizedInstantiationException.class).isPresent()) { - errorParameters.add("errorMessage", BaseSession.Error.AUTHORIZATION.message()); - errorPage = getErrorPage(errorParameters); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, BaseSession.Error.AUTHORIZATION.message()); } else if (instanceOf(e, NotAuthorizedException.class).isPresent()) { NotAuthorizedException nae = instanceOf(e, NotAuthorizedException.class).get(); if (Strings.CI.contains(nae.getMessage(), "expired")) { - errorParameters.add("errorMessage", BaseSession.Error.SESSION_EXPIRED.message()); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, BaseSession.Error.SESSION_EXPIRED.message()); } else { - errorParameters.add("errorMessage", BaseSession.Error.AUTHORIZATION.message()); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, BaseSession.Error.AUTHORIZATION.message()); } - errorPage = getErrorPage(errorParameters); } else if (instanceOf(e, SyncopeClientException.class).isPresent()) { SyncopeClientException sce = instanceOf(e, SyncopeClientException.class).get(); String errorMessage = sce.getType() == ClientExceptionType.Unknown ? String.join("", sce.getElements()) : sce.getMessage(); - errorParameters.add("errorMessage", errorMessage); - errorPage = getErrorPage(errorParameters); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, errorMessage); } else if (instanceOf(e, BadRequestException.class).isPresent() || instanceOf(e, WebServiceException.class).isPresent()) { - errorParameters.add("errorMessage", BaseSession.Error.REST.message()); - errorPage = getErrorPage(errorParameters); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, BaseSession.Error.REST.message()); } else if (instanceOf(e, PageExpiredException.class).isPresent() || !isSignedIn()) { - errorParameters.add("errorMessage", BaseSession.Error.SESSION_EXPIRED.message()); - errorPage = getErrorPage(errorParameters); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, BaseSession.Error.SESSION_EXPIRED.message()); } else { Optional cause = instanceOf(e, ForbiddenException.class); if (cause.isPresent()) { - errorParameters.add("errorMessage", cause.get().getMessage()); - errorPage = getErrorPage(errorParameters); + parameters.add(Constants.NOTIFICATION_MSG_PARAM, cause.get().getMessage()); } else { // redirect to default Wicket error page - errorPage = new ExceptionErrorPage(e, null); + return new RenderPageRequestHandler(new PageProvider(new ExceptionErrorPage(e, null))); } } - if (errorPage instanceof BaseLogin) { - try { - invalidateSession(); - } catch (Throwable t) { - // ignore - LOG.debug("Unexpected error while forcing logout after error", t); - } + try { + invalidateSession(); + } catch (Throwable t) { + // ignore + LOG.debug("Unexpected error while forcing logout after error", t); } - return new RenderPageRequestHandler(new PageProvider(errorPage)); + return new RenderPageRequestHandler(getErrorPageClass(), parameters); } protected abstract boolean isSignedIn(); protected abstract void invalidateSession(); - protected abstract IRequestablePage getErrorPage(PageParameters errorParameters); + protected abstract Class getErrorPageClass(); } diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/AbstractGroups.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/AbstractGroups.java index 17ea3f1bd53..45c9fdf27d5 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/AbstractGroups.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/AbstractGroups.java @@ -55,11 +55,12 @@ public AbstractGroups(final AnyWrapper modelObject) { // ------------------ // insert changed label if needed // ------------------ - if (modelObject instanceof UserWrapper - && UserWrapper.class.cast(modelObject).getPreviousUserTO() != null + if (modelObject instanceof final UserWrapper uw + && uw.getPreviousUserTO() != null && !ListUtils.isEqualList( - UserWrapper.class.cast(modelObject).getInnerObject().getMemberships(), - UserWrapper.class.cast(modelObject).getPreviousUserTO().getMemberships())) { + uw.getInnerObject().getMemberships(), + uw.getPreviousUserTO().getMemberships())) { + groupsContainer.add(new LabelInfo("changed", StringUtils.EMPTY)); } else { groupsContainer.add(new Label("changed", StringUtils.EMPTY)); diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml index d0af6118f1d..56e9955a7c5 100644 --- a/client/idrepo/console/pom.xml +++ b/client/idrepo/console/pom.xml @@ -48,17 +48,29 @@ under the License. com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter + org.springframework.boot - spring-boot-starter-log4j2 + spring-boot-starter-actuator + org.springframework.boot - spring-boot-starter-actuator + spring-boot-starter-security + org.springframework.boot - spring-boot-starter-security + spring-boot-starter-cache + + + com.github.ben-manes.caffeine + jcache + + + + org.springframework.boot + spring-boot-starter-log4j2 diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java index 7750d736b94..0f85093b9e6 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java @@ -18,7 +18,13 @@ */ package org.apache.syncope.client.console; +import java.time.OffsetDateTime; import java.util.List; +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.configuration.MutableConfiguration; +import javax.cache.expiry.CreatedExpiryPolicy; +import javax.cache.expiry.Duration; import org.apache.syncope.client.console.commons.AccessPolicyConfProvider; import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider; import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionsProvider; @@ -66,6 +72,7 @@ import org.apache.syncope.client.ui.commons.MIMETypesLoader; import org.apache.syncope.client.ui.commons.PreviewUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -154,6 +161,36 @@ public AccessPolicyConfProvider accessPolicyConfProvider() { return new IdRepoAccessPolicyConfProvider(); } + @ConditionalOnMissingBean + @Bean + public ServletListenerRegistrationBean sessionTimeoutListener() { + ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean<>(); + bean.setListener(new SessionTimeoutListener()); + return bean; + } + + @ConditionalOnMissingBean(name = SyncopeWebApplication.LOGGEDOUT_SESSIONID_CACHE) + @Bean(name = SyncopeWebApplication.LOGGEDOUT_SESSIONID_CACHE) + public Cache loggedoutSessionIdCache(final CacheManager cacheManager) { + return cacheManager.createCache(SyncopeWebApplication.LOGGEDOUT_SESSIONID_CACHE, + new MutableConfiguration(). + setTypes(String.class, OffsetDateTime.class). + setStoreByValue(false). + setReadThrough(true). + setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.TEN_MINUTES))); + } + + @ConditionalOnMissingBean(name = SyncopeWebApplication.DESTROYED_SESSIONID_CACHE) + @Bean(name = SyncopeWebApplication.DESTROYED_SESSIONID_CACHE) + public Cache destroyedSessionIdCache(final CacheManager cacheManager) { + return cacheManager.createCache(SyncopeWebApplication.DESTROYED_SESSIONID_CACHE, + new MutableConfiguration(). + setTypes(String.class, OffsetDateTime.class). + setStoreByValue(false). + setReadThrough(true). + setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.TEN_MINUTES))); + } + @ConditionalOnMissingBean @Bean public AccessTokenRestClient accessTokenRestClient() { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/IndicatorAjaxTimerBehavior.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SessionTimeoutListener.java similarity index 55% rename from client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/IndicatorAjaxTimerBehavior.java rename to client/idrepo/console/src/main/java/org/apache/syncope/client/console/SessionTimeoutListener.java index db6899674d4..44f967196c3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/IndicatorAjaxTimerBehavior.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SessionTimeoutListener.java @@ -16,26 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.client.console.wicket.ajax; +package org.apache.syncope.client.console; -import java.time.Duration; -import org.apache.commons.lang3.StringUtils; -import org.apache.wicket.ajax.AbstractAjaxTimerBehavior; -import org.apache.wicket.ajax.IAjaxIndicatorAware; +import com.giffing.wicket.spring.boot.starter.web.WicketWebInitializer; +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionListener; +import org.apache.wicket.Application; -/** - * An {@link AbstractAjaxTimerBehavior} not showing veil. - */ -public abstract class IndicatorAjaxTimerBehavior extends AbstractAjaxTimerBehavior implements IAjaxIndicatorAware { - - private static final long serialVersionUID = 8863750325559215077L; - - public IndicatorAjaxTimerBehavior(final Duration updateInterval) { - super(updateInterval); - } +public class SessionTimeoutListener implements HttpSessionListener { @Override - public String getAjaxIndicatorMarkupId() { - return StringUtils.EMPTY; + public void sessionDestroyed(final HttpSessionEvent event) { + ((SyncopeWebApplication) Application.get(WicketWebInitializer.WICKET_FILTERNAME)). + storeDestroyedSessionId(event.getSession().getId()); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java index a79d100fc95..74bb1be384c 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java @@ -19,8 +19,10 @@ package org.apache.syncope.client.console; import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerAutoConfig.WebSocketWicketWebInitializerAutoConfiguration; +import java.time.OffsetDateTime; import java.util.List; import java.util.Map; +import javax.cache.Cache; import org.apache.syncope.client.console.commons.AccessPolicyConfProvider; import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider; import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionsProvider; @@ -37,16 +39,19 @@ import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart; import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop; import org.apache.wicket.request.resource.IResource; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; @SpringBootApplication(proxyBeanMethods = false) @EnableConfigurationProperties(ConsoleProperties.class) +@EnableCaching public class SyncopeConsoleApplication extends SpringBootServletInitializer { public static void main(final String[] args) { @@ -78,7 +83,11 @@ public SyncopeWebApplication syncopeWebApplication( final AccessPolicyConfProvider accessPolicyConfProvider, final List policyTabProviders, final List userFormFinalizers, - final List resources) { + final List resources, + @Qualifier(SyncopeWebApplication.LOGGEDOUT_SESSIONID_CACHE) + final Cache loggedoutSessionIdCache, + @Qualifier(SyncopeWebApplication.DESTROYED_SESSIONID_CACHE) + final Cache destroyedSessionIdCache) { return new SyncopeWebApplication( props, @@ -92,7 +101,9 @@ public SyncopeWebApplication syncopeWebApplication( accessPolicyConfProvider, policyTabProviders, userFormFinalizers, - resources); + resources, + loggedoutSessionIdCache, + destroyedSessionIdCache); } @ConditionalOnMissingBean diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java index d55c451f3c4..53586cd8116 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java @@ -65,6 +65,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.core.task.TaskRejectedException; +import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler; import org.springframework.util.CollectionUtils; public class SyncopeConsoleSession extends AuthenticatedWebSession implements BaseSession { @@ -133,7 +134,7 @@ public SyncopeConsoleSession(final Request request) { clientFactory = SyncopeWebApplication.get().newClientFactory(); - executor = new SimpleAsyncTaskExecutor(); + executor = new SimpleAsyncTaskScheduler(); executor.setVirtualThreads(true); } @@ -270,12 +271,15 @@ public void cleanup() { @Override public void invalidate() { + SyncopeWebApplication.get().storeLoggedOutSessionId(getId()); + if (getJWT() != null) { if (client != null) { client.logout(); } cleanup(); } + super.invalidate(); } @@ -324,16 +328,15 @@ public boolean owns(final String entitlements, final String... realms) { map(RealmsUtils::getFullPath).collect(Collectors.toSet()); if (requested.isEmpty()) { return !owned.isEmpty(); - } else { - for (String realm : requested) { - if (realm.startsWith(SyncopeConstants.ROOT_REALM)) { - owns |= owned.stream().anyMatch(realm::startsWith); - } else { - owns |= owned.contains(realm); - } - } } + for (String realm : requested) { + if (realm.startsWith(SyncopeConstants.ROOT_REALM)) { + owns |= owned.stream().anyMatch(realm::startsWith); + } else { + owns |= owned.contains(realm); + } + } return owns; } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java index ade79641a51..ab755d4a9f7 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java @@ -23,9 +23,12 @@ import de.agilecoders.wicket.core.settings.BootstrapSettings; import de.agilecoders.wicket.core.settings.IBootstrapSettings; import de.agilecoders.wicket.core.settings.SingleThemeProvider; +import jakarta.servlet.http.Cookie; +import java.time.OffsetDateTime; import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; +import javax.cache.Cache; import org.apache.syncope.client.console.annotations.UserFormFinalize; import org.apache.syncope.client.console.commons.AccessPolicyConfProvider; import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider; @@ -45,6 +48,8 @@ import org.apache.syncope.client.console.wizards.any.UserFormFinalizer; import org.apache.syncope.client.lib.SyncopeAnonymousClient; import org.apache.syncope.client.lib.SyncopeClientFactoryBean; +import org.apache.syncope.client.ui.commons.BaseLogin; +import org.apache.syncope.client.ui.commons.BaseSession; import org.apache.syncope.client.ui.commons.BaseWebApplication; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener; @@ -55,6 +60,7 @@ import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.wicket.Page; +import org.apache.wicket.RestartResponseAtInterceptPageException; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy; @@ -63,21 +69,28 @@ import org.apache.wicket.protocol.http.servlet.XForwardedRequestWrapperFactory; import org.apache.wicket.protocol.ws.WebSocketAwareResourceIsolationRequestCycleListener; import org.apache.wicket.protocol.ws.api.WebSocketResponse; -import org.apache.wicket.request.component.IRequestablePage; import org.apache.wicket.request.cycle.IRequestCycleListener; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.http.WebResponse; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.IResource; import org.apache.wicket.request.resource.ResourceReference; +import org.apache.wicket.util.cookies.CookieUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.support.AopUtils; +import org.wicketstuff.kendo.ui.widget.notification.Notification; public class SyncopeWebApplication extends WicketBootSecuredWebApplication implements BaseWebApplication { protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWebApplication.class); + public static final String LOGGEDOUT_SESSIONID_CACHE = "loggedoutSessionIdCache"; + + public static final String DESTROYED_SESSIONID_CACHE = "destroyedSessionIdCache"; + + protected static final CookieUtils COOKIE_UTILS = new CookieUtils(); + public static SyncopeWebApplication get() { return (SyncopeWebApplication) WebApplication.get(); } @@ -108,6 +121,10 @@ public static SyncopeWebApplication get() { protected final List resources; + protected final Cache loggedoutSessionIdCache; + + protected final Cache destroyedSessionIdCache; + public SyncopeWebApplication( final ConsoleProperties props, final ClassPathScanImplementationLookup lookup, @@ -121,7 +138,9 @@ public SyncopeWebApplication( final AccessPolicyConfProvider accessPolicyConfProvider, final List policyTabProviders, final List userFormFinalizers, - final List resources) { + final List resources, + final Cache loggedoutSessionIdCache, + final Cache destroyedSessionIdCache) { this.props = props; this.lookup = lookup; @@ -136,6 +155,8 @@ public SyncopeWebApplication( this.policyTabProviders = policyTabProviders; this.userFormFinalizers = userFormFinalizers; this.resources = resources; + this.loggedoutSessionIdCache = loggedoutSessionIdCache; + this.destroyedSessionIdCache = destroyedSessionIdCache; } protected SyncopeUIRequestCycleListener buildSyncopeUIRequestCycleListener() { @@ -152,8 +173,8 @@ protected void invalidateSession() { } @Override - protected IRequestablePage getErrorPage(final PageParameters errorParameters) { - return new Login(errorParameters); + protected Class getErrorPageClass() { + return Login.class; } }; } @@ -312,6 +333,30 @@ public int getMaxUploadFileSizeMB() { return props.getMaxUploadFileSizeMB(); } + public void storeLoggedOutSessionId(final String sessionId) { + loggedoutSessionIdCache.put(sessionId, OffsetDateTime.now()); + } + + public void storeDestroyedSessionId(final String sessionId) { + destroyedSessionIdCache.put(sessionId, OffsetDateTime.now()); + } + + @Override + public void restartResponseAtSignInPage() { + PageParameters parameters = new PageParameters(); + + Optional.ofNullable(COOKIE_UTILS.getCookie(COOKIE_UTILS.getSessionIdCookieName(this))). + map(Cookie::getValue). + filter(sessionId -> destroyedSessionIdCache.containsKey(sessionId) + && !loggedoutSessionIdCache.containsKey(sessionId)). + ifPresent(sessionId -> { + parameters.add(Constants.NOTIFICATION_MSG_PARAM, BaseSession.Error.SESSION_EXPIRED.message()); + parameters.add(Constants.NOTIFICATION_LEVEL_PARAM, Notification.WARNING); + }); + + throw new RestartResponseAtInterceptPageException(getSignInPageClass(), parameters); + } + public boolean fullRealmsTree(final RealmRestClient restClient) { if (props.getRealmsFullTreeThreshold() <= 0) { return false; @@ -361,7 +406,7 @@ public List getFormFinalizers(final AjaxWizard.Mode mode) { } return annotation.mode() == mode; - }).collect(Collectors.toList()); + }).toList(); } public AccessPolicyConfProvider getAccessPolicyConfProvider() { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java index b46f51eccf0..d2605a3207e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/batch/BatchContent.java @@ -166,45 +166,38 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { String etag = anyRestClient.read(key).getETagValue(); switch (action) { - case DEPROVISION: + case DEPROVISION -> results.putAll(anyRestClient.deassociate( ResourceDeassociationAction.DEPROVISION, etag, key, value)); - break; - case UNASSIGN: + case UNASSIGN -> results.putAll(anyRestClient.deassociate( ResourceDeassociationAction.UNASSIGN, etag, key, value)); - break; - case UNLINK: + case UNLINK -> results.putAll(anyRestClient.deassociate( ResourceDeassociationAction.UNLINK, etag, key, value)); - break; - case ASSIGN: + case ASSIGN -> results.putAll(anyRestClient.associate( ResourceAssociationAction.ASSIGN, etag, key, value)); - break; - case LINK: + case LINK -> results.putAll(anyRestClient.associate( ResourceAssociationAction.LINK, etag, key, value)); - break; - case PROVISION: + case PROVISION -> results.putAll(anyRestClient.associate( ResourceAssociationAction.PROVISION, etag, key, value)); - break; - case SUSPEND: + case SUSPEND -> results.putAll(((UserRestClient) anyRestClient).suspend(etag, key, value)); - break; - case REACTIVATE: + case REACTIVATE -> results.putAll(((UserRestClient) anyRestClient).reactivate(etag, key, value)); - break; - default: + default -> { + } } }); } else { @@ -224,7 +217,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { Set deletedAnys = new HashSet<>(); switch (action) { - case MUSTCHANGEPASSWORD: + case MUSTCHANGEPASSWORD -> items.forEach(item -> { UserTO user = (UserTO) item; @@ -235,9 +228,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { batchUserService.update(req); }); - break; - case SUSPEND: + case SUSPEND -> items.forEach(item -> { UserTO user = (UserTO) item; @@ -248,9 +240,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { batchUserService.status(req); }); - break; - case REACTIVATE: + case REACTIVATE -> items.forEach(item -> { UserTO user = (UserTO) item; @@ -261,9 +252,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { batchUserService.status(req); }); - break; - case DELETE: + case DELETE -> items.forEach(item -> { if (singleItem instanceof AnyTO) { AnyTO any = (AnyTO) item; @@ -285,18 +275,15 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { } }); - break; - - case DRYRUN: + case DRYRUN -> items.forEach(item -> { TaskTO task = (TaskTO) item; batchTaskService.execute( new ExecSpecs.Builder().dryRun(true).key(task.getKey()).build()); }); - break; - case EXECUTE: + case EXECUTE -> items.forEach(item -> { if (singleItem instanceof TaskTO) { TaskTO task = (TaskTO) item; @@ -310,9 +297,9 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { new ExecSpecs.Builder().key(report.getKey()).build()); } }); - break; - default: + default -> { + } } results = CastUtils.cast(Map.class.cast( diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java index 6b26769a2ee..f8c024e887e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java @@ -266,9 +266,9 @@ public List> getClasses(final Class reference) { } @SuppressWarnings("unchecked") - public List>> getExtAlertWidgetClasses() { + public List> getExtAlertWidgetClasses() { return classes.getOrDefault(ExtAlertWidget.class.getName(), List.of()).stream(). - map(clazz -> (Class>) clazz). + map(clazz -> (Class) clazz). collect(Collectors.toList()); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java index 6a1b8fe56b0..2f793bd21c3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java @@ -433,19 +433,19 @@ public void onComponentTag(final Component component, final ComponentTag tag) { } // Extensions - List>> extAlertWidgetClasses = + List> extAlertWidgetClasses = SyncopeWebApplication.get().getLookup().getExtAlertWidgetClasses(); - ListView>> extAlertWidgets = new ListView<>( + ListView> extAlertWidgets = new ListView<>( "extAlertWidgets", extAlertWidgetClasses) { private static final long serialVersionUID = -9112553137618363167L; @Override - protected void populateItem(final ListItem>> item) { + protected void populateItem(final ListItem> item) { try { - Constructor> constructor = + Constructor constructor = item.getModelObject().getDeclaredConstructor(String.class, PageReference.class); - ExtAlertWidget widget = constructor.newInstance("extAlertWidget", getPageReference()); + ExtAlertWidget widget = constructor.newInstance("extAlertWidget", getPageReference()); SyncopeConsoleSession.get().setAttribute(widget.getClass().getName(), widget); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java index d2554b2f393..cbb251e84cc 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java @@ -156,8 +156,7 @@ public void onEvent(final IEvent event) { ChosenRealm choosenRealm = ChosenRealm.class.cast(event.getPayload()); updateRealmContent(choosenRealm.getObj(), 0); choosenRealm.getTarget().add(content); - } else if (event.getPayload() instanceof AjaxWizard.NewItemEvent) { - AjaxWizard.NewItemEvent newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload()); + } else if (event.getPayload() instanceof AjaxWizard.NewItemEvent newItemEvent) { WizardModalPanel modalPanel = newItemEvent.getModalPanel(); if (event.getPayload() instanceof AjaxWizard.NewItemActionEvent && modalPanel != null) { @@ -202,10 +201,8 @@ protected void onClickTemplate(final AjaxRequestTarget target) { @Override protected void setWindowClosedReloadCallback(final BaseModal modal) { modal.setWindowClosedCallback(target -> { - if (modal.getContent() instanceof ResultPanel) { - Object result = ResultPanel.class.cast(modal.getContent()).getResult(); - - RealmTO newRealmTO = RealmTO.class.cast(ProvisioningResult.class.cast(result).getEntity()); + if (modal.getContent() instanceof ResultPanel rp) { + RealmTO newRealmTO = RealmTO.class.cast(ProvisioningResult.class.cast(rp.getResult()).getEntity()); // reload realmChoicePanel label too - SYNCOPE-1151 target.add(realmChoicePanel.reloadRealmTree(target, Model.of(newRealmTO))); realmChoicePanel.setCurrentRealm(newRealmTO); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java index e0868673ea1..7abaa5f20b0 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java @@ -311,8 +311,8 @@ protected Panel createDirectoryPanel( @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof SearchClausePanel.SearchEvent) { - AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof SearchClausePanel.SearchEvent payload) { + AjaxRequestTarget target = payload.getTarget(); send(AnyPanel.this.directoryPanel, Broadcast.BREADTH, new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target)); @@ -322,7 +322,7 @@ public void onEvent(final IEvent event) { : String.format("$dynRealms=~%s;", realmTO.getKey()); switch (anyTypeTO.getKind()) { - case USER: + case USER -> UserDirectoryPanel.class.cast(AnyPanel.this.directoryPanel).search( precond + SearchUtils.buildFIQL( AnyPanel.this.searchPanel.getModel().getObject(), @@ -330,9 +330,8 @@ public void onEvent(final IEvent event) { AnyPanel.this.searchPanel.getAvailableSchemaTypes(), SearchUtils.NO_CUSTOM_CONDITION), target); - break; - case GROUP: + case GROUP -> GroupDirectoryPanel.class.cast(AnyPanel.this.directoryPanel).search( precond + SearchUtils.buildFIQL( AnyPanel.this.searchPanel.getModel().getObject(), @@ -340,9 +339,8 @@ public void onEvent(final IEvent event) { AnyPanel.this.searchPanel.getAvailableSchemaTypes(), SearchUtils.NO_CUSTOM_CONDITION), target); - break; - case ANY_OBJECT: + case ANY_OBJECT -> AnyObjectDirectoryPanel.class.cast(AnyPanel.this.directoryPanel).search( precond + SearchUtils.buildFIQL( AnyPanel.this.searchPanel.getModel().getObject(), @@ -350,9 +348,9 @@ public void onEvent(final IEvent event) { AnyPanel.this.searchPanel.getAvailableSchemaTypes(), SearchUtils.NO_CUSTOM_CONDITION), target); - break; - default: + default -> { + } } } else { super.onEvent(event); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/CommandDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/CommandDirectoryPanel.java index c6683bba7f0..f96c484d64a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/CommandDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/CommandDirectoryPanel.java @@ -139,9 +139,7 @@ protected Panel customResultBody(final String panelId, final CommandTO item, fin @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof KeywordSearchEvent) { - KeywordSearchEvent payload = KeywordSearchEvent.class.cast(event.getPayload()); - + if (event.getPayload() instanceof KeywordSearchEvent payload) { keyword = payload.getKeyword(); if (StringUtils.isNotBlank(keyword)) { if (!Strings.CS.startsWith(keyword, "*")) { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardOverviewPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardOverviewPanel.java index 034b446e037..a0d167e205c 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardOverviewPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardOverviewPanel.java @@ -18,21 +18,19 @@ */ package org.apache.syncope.client.console.panels; -import java.time.Duration; -import java.time.temporal.ChronoUnit; import org.apache.commons.lang3.tuple.Triple; import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; +import org.apache.syncope.client.console.wicket.ws.RefreshWebSocketBehavior; import org.apache.syncope.client.console.widgets.AnyByRealmWidget; import org.apache.syncope.client.console.widgets.CompletenessWidget; import org.apache.syncope.client.console.widgets.LoadWidget; import org.apache.syncope.client.console.widgets.NumberWidget; import org.apache.syncope.client.console.widgets.UsersByStatusWidget; import org.apache.syncope.common.lib.info.NumbersInfo; -import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; public class DashboardOverviewPanel extends Panel { @@ -100,36 +98,36 @@ public DashboardOverviewPanel(final String id) { load = new LoadWidget("load", SyncopeConsoleSession.get().getAnonymousClient().system()); container.add(load); - container.add(new IndicatorAjaxTimerBehavior(Duration.of(60, ChronoUnit.SECONDS)) { + container.add(new RefreshWebSocketBehavior() { - private static final long serialVersionUID = -4426283634345968585L; + private static final long serialVersionUID = -7095269057058900157L; @Override - protected void onTimer(final AjaxRequestTarget target) { + protected void onTimer(final WebSocketRequestHandler handler) { NumbersInfo numbers = SyncopeConsoleSession.get().getAnonymousClient().numbers(); if (totalUsers.refresh(numbers.getTotalUsers())) { - target.add(totalUsers); + handler.add(totalUsers); } if (totalGroups.refresh(numbers.getTotalGroups())) { - target.add(totalGroups); + handler.add(totalGroups); } Triple updatedBuild = buildTotalAny1OrRoles(numbers); if (totalAny1OrRoles.refresh(updatedBuild.getLeft())) { - target.add(totalAny1OrRoles); + handler.add(totalAny1OrRoles); } updatedBuild = buildTotalAny2OrResources(numbers); if (totalAny2OrResources.refresh(updatedBuild.getLeft())) { - target.add(totalAny2OrResources); + handler.add(totalAny2OrResources); } if (usersByStatus.refresh(numbers.getUsersByStatus())) { - target.add(usersByStatus); + handler.add(usersByStatus); } if (completeness.refresh(numbers.getConfCompleteness())) { - target.add(completeness); + handler.add(completeness); } if (anyByRealm.refresh( @@ -140,13 +138,13 @@ protected void onTimer(final AjaxRequestTarget target) { numbers.getAnyType2(), numbers.getAny2ByRealm())) { - target.add(anyByRealm); + handler.add(anyByRealm); } load.refresh(SyncopeConsoleSession.get().getAnonymousClient().system()); - target.add(load); + handler.add(load); } - }); + }.schedule(60)); } private static Triple buildTotalAny1OrRoles(final NumbersInfo numbers) { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/LogsPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/LogsPanel.java index 6726fa14c55..238512faa71 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/LogsPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/LogsPanel.java @@ -107,7 +107,7 @@ protected void onSubmit(final AjaxRequestTarget target) { protected Component getValueComponent(final String key, final LoggerConf loggerConf) { if ("level".equalsIgnoreCase(key)) { AjaxDropDownChoicePanel loggerLevel = new AjaxDropDownChoicePanel<>( - "field", getString("level"), Model.of(loggerConf.getLevel()), false); + "field", getString("level"), Model.of(loggerConf.getLevel()), false); MetaDataRoleAuthorizationStrategy.authorize(loggerLevel, ENABLE, IdRepoEntitlement.LOGGER_UPDATE); loggerLevel.hideLabel(); @@ -152,8 +152,7 @@ protected void onUpdate(final AjaxRequestTarget target) { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof LoggerConfSearchEvent) { - LoggerConfSearchEvent payload = LoggerConfSearchEvent.class.cast(event.getPayload()); + if (event.getPayload() instanceof LoggerConfSearchEvent payload) { AjaxRequestTarget target = payload.getTarget(); String keyword = payload.getKeyword(); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java index 53666bd93cd..c09831636d5 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/SchemaTypePanel.java @@ -187,9 +187,7 @@ public void onClick(final AjaxRequestTarget target, final SchemaTO ignore) { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof KeywordSearchEvent) { - KeywordSearchEvent payload = KeywordSearchEvent.class.cast(event.getPayload()); - + if (event.getPayload() instanceof KeywordSearchEvent payload) { keyword = payload.getKeyword(); if (StringUtils.isNotBlank(keyword)) { if (!Strings.CS.startsWith(keyword, "*")) { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java index ea1f19297c4..cb2fe697202 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java @@ -19,8 +19,6 @@ package org.apache.syncope.client.console.reports; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; -import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -35,13 +33,13 @@ import org.apache.syncope.client.console.panels.MultilevelPanel; import org.apache.syncope.client.console.rest.ImplementationRestClient; import org.apache.syncope.client.console.rest.ReportRestClient; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn; 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.form.ActionLink; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType; import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; +import org.apache.syncope.client.console.wicket.ws.RefreshWebSocketBehavior; import org.apache.syncope.client.console.widgets.JobActionPanel; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.MIMETypesLoader; @@ -68,6 +66,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.spring.injection.annot.SpringBean; /** @@ -99,16 +98,16 @@ protected ReportDirectoryPanel(final ReportRestClient restClient, final PageRefe modal.size(Modal.Size.Large); initResultTable(); - container.add(new IndicatorAjaxTimerBehavior(Duration.of(10, ChronoUnit.SECONDS)) { + container.add(new RefreshWebSocketBehavior() { private static final long serialVersionUID = -4661303265651934868L; @Override - protected void onTimer(final AjaxRequestTarget target) { + protected void onTimer(final WebSocketRequestHandler handler) { container.modelChanged(); - target.add(container); + handler.add(container); } - }); + }.schedule(10)); startAt = new ReportStartAtTogglePanel(container, pageRef); addInnerObject(startAt); @@ -180,9 +179,9 @@ public String getCssClass() { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof JobActionPanel.JobActionPayload) { + if (event.getPayload() instanceof JobActionPanel.JobActionPayload payload) { container.modelChanged(); - JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container); + payload.getTarget().add(container); } else { super.onEvent(event); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java index 2824293f615..fad5cfd2d27 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java @@ -18,25 +18,24 @@ */ package org.apache.syncope.client.console.tasks; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.syncope.client.console.panels.MultilevelPanel; import org.apache.syncope.client.console.rest.TaskRestClient; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; +import org.apache.syncope.client.console.wicket.ws.RefreshWebSocketBehavior; import org.apache.syncope.client.console.widgets.JobActionPanel; import org.apache.syncope.common.lib.to.InboundTaskTO; import org.apache.syncope.common.lib.to.ProvisioningTaskTO; import org.apache.syncope.common.lib.to.PushTaskTO; import org.apache.syncope.common.lib.types.TaskType; 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.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; /** * Tasks page. @@ -76,16 +75,16 @@ protected ProvisioningTaskDirectoryPanel( enableUtilityButton(); super.initResultTable(); - container.add(new IndicatorAjaxTimerBehavior(java.time.Duration.of(10, ChronoUnit.SECONDS)) { + container.add(new RefreshWebSocketBehavior() { private static final long serialVersionUID = -4661303265651934868L; @Override - protected void onTimer(final AjaxRequestTarget target) { + protected void onTimer(final WebSocketRequestHandler handler) { container.modelChanged(); - target.add(container); + handler.add(container); } - }); + }.schedule(10)); } @Override diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java index 079c3909904..afcf37b27bb 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java @@ -20,8 +20,6 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.io.Serializable; -import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -35,7 +33,6 @@ import org.apache.syncope.client.console.panels.MultilevelPanel; import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.client.console.rest.TaskRestClient; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn; 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; @@ -43,6 +40,7 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType; import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; +import org.apache.syncope.client.console.wicket.ws.RefreshWebSocketBehavior; import org.apache.syncope.client.console.widgets.JobActionPanel; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.panels.ModalPanel; @@ -70,6 +68,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.spring.injection.annot.SpringBean; /** @@ -121,16 +120,16 @@ protected SchedTaskDirectoryPanel( initResultTable(); - container.add(new IndicatorAjaxTimerBehavior(Duration.of(10, ChronoUnit.SECONDS)) { + container.add(new RefreshWebSocketBehavior() { private static final long serialVersionUID = -4661303265651934868L; @Override - protected void onTimer(final AjaxRequestTarget target) { + protected void onTimer(final WebSocketRequestHandler handler) { container.modelChanged(); - target.add(container); + handler.add(container); } - }); + }.schedule(10)); startAt = new TaskStartAtTogglePanel(container, pageRef); addInnerObject(startAt); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java index 23061e9caf1..efe40b527e0 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java @@ -108,10 +108,9 @@ public IModel model(final T object) { @Override public void onEvent(final IEvent event) { super.onEvent(event); - if (event.getPayload() instanceof ExitEvent) { - AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof ExitEvent exitEvent) { baseModal.show(false); - baseModal.close(target); + baseModal.close(exitEvent.getTarget()); } } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java index 983c6f039d6..57a6e982689 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java @@ -153,8 +153,8 @@ private Fragment getEmptyFragment() { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof ActionLinkToggleCloseEventPayload) { - close(ActionLinkToggleCloseEventPayload.class.cast(event.getPayload()).getTarget()); + if (event.getPayload() instanceof final ActionLinkToggleCloseEventPayload payload) { + close(payload.getTarget()); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ws/RefreshWebSocketBehavior.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ws/RefreshWebSocketBehavior.java new file mode 100644 index 00000000000..a95073f32a4 --- /dev/null +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/ws/RefreshWebSocketBehavior.java @@ -0,0 +1,77 @@ +/* + * 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.wicket.ws; + +import com.giffing.wicket.spring.boot.starter.web.WicketWebInitializer; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.wicket.Application; +import org.apache.wicket.protocol.ws.WebSocketSettings; +import org.apache.wicket.protocol.ws.api.IWebSocketConnection; +import org.apache.wicket.protocol.ws.api.WebSocketBehavior; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; +import org.apache.wicket.protocol.ws.api.message.ConnectedMessage; +import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; +import org.apache.wicket.protocol.ws.api.registry.IKey; + +public abstract class RefreshWebSocketBehavior extends WebSocketBehavior { + + private static final long serialVersionUID = 5636572627689425575L; + + protected final Mutable> websocketInfo = new MutableObject<>(); + + @Override + protected void onConnect(final ConnectedMessage message) { + websocketInfo.setValue(Pair.of(message.getSessionId(), message.getKey())); + } + + protected abstract void onTimer(WebSocketRequestHandler handler); + + @Override + protected void onPush(final WebSocketRequestHandler handler, final IWebSocketPushMessage message) { + onTimer(handler); + } + + public RefreshWebSocketBehavior schedule(final long periodSeconds) { + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, Thread.ofVirtual().factory()); + executor.scheduleAtFixedRate(() -> { + if (websocketInfo.get() == null) { + return; + } + + Application application = Application.get(WicketWebInitializer.WICKET_FILTERNAME); + IWebSocketConnection wsConnection = WebSocketSettings.Holder.get(application).getConnectionRegistry(). + getConnection(application, websocketInfo.get().getLeft(), websocketInfo.get().getRight()); + if (wsConnection != null && wsConnection.isOpen()) { + wsConnection.sendMessage(new IWebSocketPushMessage() { + + private static final long serialVersionUID = -4425174034118785972L; + + }); + } else { + executor.shutdownNow(); + } + }, 0, periodSeconds, TimeUnit.SECONDS); + + return this; + } +} diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/AlertWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/AlertWidget.java index 30328df69ae..93086bb200a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/AlertWidget.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/AlertWidget.java @@ -19,41 +19,29 @@ package org.apache.syncope.client.console.widgets; import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; -import java.io.Serializable; -import java.util.List; import org.apache.wicket.markup.ComponentTag; -import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.AbstractLink; import org.apache.wicket.markup.html.panel.Panel; -import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class AlertWidget extends Panel { +public abstract class AlertWidget extends Panel { private static final long serialVersionUID = 7667120094526529934L; protected static final Logger LOG = LoggerFactory.getLogger(AlertWidget.class); - protected static final int MAX_SIZE = 5; - protected final Label linkAlertsNumber; protected final Label headerAlertsNumber; - protected final WebMarkupContainer latestAlertsList; - - protected IModel> latestAlerts; - public AlertWidget(final String id) { super(id); - this.latestAlerts = getLatestAlerts(); - setOutputMarkupId(true); - final LoadableDetachableModel size = new LoadableDetachableModel<>() { + LoadableDetachableModel size = new LoadableDetachableModel<>() { private static final long serialVersionUID = 7474274077691068779L; @@ -90,21 +78,12 @@ protected void onComponentTag(final ComponentTag tag) { add(linkAlertsNumber.setOutputMarkupId(true)); headerAlertsNumber = new Label("number", size); - headerAlertsNumber.setOutputMarkupId(true); - add(headerAlertsNumber); + add(headerAlertsNumber.setOutputMarkupId(true)); add(getEventsLink("alertsLink")); - - latestAlertsList = new WebMarkupContainer("latestAlertsList"); - latestAlertsList.setOutputMarkupId(true); - add(latestAlertsList); - } - - protected long getLatestAlertsSize() { - return latestAlerts.getObject().size(); } - protected abstract IModel> getLatestAlerts(); + protected abstract long getLatestAlertsSize(); protected abstract AbstractLink getEventsLink(String linkid); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ExtAlertWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ExtAlertWidget.java index 3d354899165..56039dd9933 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ExtAlertWidget.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ExtAlertWidget.java @@ -18,10 +18,11 @@ */ package org.apache.syncope.client.console.widgets; -import java.io.Serializable; +import org.apache.syncope.client.console.wicket.ws.RefreshWebSocketBehavior; import org.apache.wicket.PageReference; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; -public abstract class ExtAlertWidget extends AlertWidget { +public abstract class ExtAlertWidget extends AlertWidget { private static final long serialVersionUID = -5622060468533516192L; @@ -30,5 +31,22 @@ public abstract class ExtAlertWidget extends AlertWidget public ExtAlertWidget(final String id, final PageReference pageRef) { super(id); this.pageRef = pageRef; + + add(new RefreshWebSocketBehavior() { + + private static final long serialVersionUID = -7095269057058900157L; + + @Override + protected void onTimer(final WebSocketRequestHandler handler) { + long latestAlterts = getLatestAlertsSize(); + if (!String.valueOf(latestAlterts).equals(linkAlertsNumber.getDefaultModelObjectAsString())) { + linkAlertsNumber.setDefaultModelObject(latestAlterts); + handler.add(linkAlertsNumber); + + headerAlertsNumber.setDefaultModelObject(latestAlterts); + handler.add(headerAlertsNumber); + } + } + }.schedule(30)); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ItemTransformerWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ItemTransformerWidget.java index 3f748239417..c31cf85915e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ItemTransformerWidget.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/ItemTransformerWidget.java @@ -20,16 +20,13 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType; -import java.util.List; import org.apache.syncope.client.console.wizards.mapping.ItemTransformersTogglePanel; import org.apache.syncope.common.lib.to.Item; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.link.AbstractLink; -import org.apache.wicket.model.IModel; -import org.apache.wicket.model.util.ListModel; -public class ItemTransformerWidget extends AlertWidget { +public class ItemTransformerWidget extends AlertWidget { private static final long serialVersionUID = 7667120094526529934L; @@ -45,20 +42,11 @@ public ItemTransformerWidget( super(id); this.item = item; this.transformers = transformers; - setOutputMarkupId(true); } @Override - protected IModel> getLatestAlerts() { - return new ListModel<>() { - - private static final long serialVersionUID = 1232998477036705088L; - - @Override - public List getObject() { - return item.getTransformers(); - } - }; + protected long getLatestAlertsSize() { + return item.getTransformers().size(); } @Override diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JEXLTransformerWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JEXLTransformerWidget.java index 64b809e679d..5bb6ad85c7d 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JEXLTransformerWidget.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JEXLTransformerWidget.java @@ -20,18 +20,14 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType; -import java.util.ArrayList; -import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.wizards.mapping.JEXLTransformersTogglePanel; import org.apache.syncope.common.lib.to.Item; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.link.AbstractLink; -import org.apache.wicket.model.IModel; -import org.apache.wicket.model.util.ListModel; -public class JEXLTransformerWidget extends AlertWidget { +public class JEXLTransformerWidget extends AlertWidget { private static final long serialVersionUID = 7667120094526529934L; @@ -45,32 +41,21 @@ public JEXLTransformerWidget( final JEXLTransformersTogglePanel transformers) { super(id); - setOutputMarkupId(true); this.item = item; this.transformers = transformers; - - this.latestAlertsList.setVisible(false); } @Override - protected IModel> getLatestAlerts() { - return new ListModel<>() { - - private static final long serialVersionUID = -2583290457773357445L; - - @Override - public List getObject() { - List result = new ArrayList<>(); - if (StringUtils.isNotBlank(item.getPropagationJEXLTransformer())) { - result.add(item.getPropagationJEXLTransformer()); - } - if (StringUtils.isNotBlank(item.getPullJEXLTransformer())) { - result.add(item.getPullJEXLTransformer()); - } - return result; - } - }; + protected long getLatestAlertsSize() { + long size = 0; + if (StringUtils.isNotBlank(item.getPropagationJEXLTransformer())) { + size++; + } + if (StringUtils.isNotBlank(item.getPullJEXLTransformer())) { + size++; + } + return size; } @Override diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java index f5556a55566..fd274ac0f50 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java @@ -21,8 +21,6 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel; import java.io.Serializable; -import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -43,7 +41,6 @@ import org.apache.syncope.client.console.rest.ReportRestClient; import org.apache.syncope.client.console.rest.TaskRestClient; import org.apache.syncope.client.console.tasks.SchedTaskWizardBuilder; -import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn; import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; @@ -51,6 +48,7 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel; import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; +import org.apache.syncope.client.console.wicket.ws.RefreshWebSocketBehavior; import org.apache.syncope.client.console.wizards.WizardMgtPanel; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.MIMETypesLoader; @@ -85,6 +83,7 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.spring.injection.annot.SpringBean; public class JobWidget extends BaseWidget { @@ -174,19 +173,19 @@ public JobWidget(final String id, final PageReference pageRef) { recent = getUpdatedRecent(); container = new WebMarkupContainer("jobContainer"); - container.add(new IndicatorAjaxTimerBehavior(Duration.of(10, ChronoUnit.SECONDS)) { + container.add(new RefreshWebSocketBehavior() { private static final long serialVersionUID = 7298597675929755960L; @Override - protected void onTimer(final AjaxRequestTarget target) { + protected void onTimer(final WebSocketRequestHandler handler) { List updatedAvailable = getUpdatedAvailable(); if (!updatedAvailable.equals(available)) { available.clear(); available.addAll(updatedAvailable); if (availableJobsPanel != null) { availableJobsPanel.modelChanged(); - target.add(availableJobsPanel); + handler.add(availableJobsPanel); } } @@ -196,11 +195,11 @@ protected void onTimer(final AjaxRequestTarget target) { recent.addAll(updatedRecent); if (recentExecPanel != null) { recentExecPanel.modelChanged(); - target.add(recentExecPanel); + handler.add(recentExecPanel); } } } - }); + }.schedule(10)); add(container); container.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList(pageRef))); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java index cc3feb98bd4..c9ce1a10743 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java @@ -29,11 +29,9 @@ import org.apache.syncope.client.console.rest.UserRestClient; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.common.lib.to.AnyTypeTO; -import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.wicket.Component; import org.apache.wicket.PageReference; -import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.event.IEvent; import org.apache.wicket.extensions.wizard.WizardStep; import org.apache.wicket.model.IModel; @@ -82,25 +80,27 @@ public UserSelectionWizardStep( @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof SearchClausePanel.SearchEvent) { - AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget(); - String fiql = SearchUtils.buildFIQL( - userSearchPanel.getModel().getObject(), SyncopeClient.getUserSearchConditionBuilder()); - userDirectoryPanel.search(fiql, target); - } else if (event.getPayload() instanceof AnySelectionDirectoryPanel.ItemSelection) { - @SuppressWarnings("unchecked") - AnySelectionDirectoryPanel.ItemSelection payload = - (AnySelectionDirectoryPanel.ItemSelection) event.getPayload(); - - UserTO selected = payload.getSelection(); - this.model.setObject(selected.getKey()); - - String tableId = ((Component) event.getSource()). - get("container:content:searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable"). - getMarkupId(); - String js = "$('#" + tableId + " tr').removeClass('active');"; - js += "$('#" + tableId + " td[title=" + selected.getKey() + "]').parent().addClass('active');"; - payload.getTarget().prependJavaScript(js); + switch (event.getPayload()) { + case SearchClausePanel.SearchEvent payload -> { + String fiql = SearchUtils.buildFIQL( + userSearchPanel.getModel().getObject(), SyncopeClient.getUserSearchConditionBuilder()); + userDirectoryPanel.search(fiql, payload.getTarget()); + } + + case AnySelectionDirectoryPanel.ItemSelection payload -> { + this.model.setObject(payload.getSelection().getKey()); + + String tableId = ((Component) event.getSource()). + get("container:content:searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable"). + getMarkupId(); + String js = "$('#" + tableId + " tr').removeClass('active');"; + js += "$('#" + tableId + " td[title=" + payload.getSelection().getKey() + + "]').parent().addClass('active');"; + payload.getTarget().prependJavaScript(js); + } + + default -> { + } } } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java index 436ff083fe2..ee8a096d395 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java @@ -167,10 +167,9 @@ public String getActualId() { @Override @SuppressWarnings("unchecked") public void onEvent(final IEvent event) { - if (event.getPayload() instanceof ExitEvent) { - AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof final ExitEvent payload) { // default behaviour: change it catching the event if needed - modal.close(target); + modal.close(payload.getTarget()); } else if (event.getPayload() instanceof AjaxWizard.NewItemEvent) { AjaxWizard.NewItemEvent newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload()); Optional target = newItemEvent.getTarget(); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectTemplateWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectTemplateWizardBuilder.java index 709626917a2..9fb5370199b 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectTemplateWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectTemplateWizardBuilder.java @@ -54,9 +54,8 @@ public AnyObjectTemplateWizardBuilder( } else { AnyObjectTO anyObjectTO = new AnyObjectTO(); anyObjectTO.setType(anyType); - if (templatable instanceof RealmTO) { - anyObjectTO.setRealm( - String.format("'%s'", RealmsUtils.getFullPath(RealmTO.class.cast(templatable).getFullPath()))); + if (templatable instanceof final RealmTO realmTO) { + anyObjectTO.setRealm(String.format("'%s'", RealmsUtils.getFullPath(realmTO.getFullPath()))); } setItem(new AnyObjectWrapper(anyObjectTO)); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java index bf322d4b3bf..f62a8870de3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java @@ -103,16 +103,15 @@ protected WizardModel buildModelSteps(final AnyWrapper modelObject, final Wiz // optional details panel step addOptionalDetailsPanel(modelObject).ifPresent(wizardModel::add); - if ((this instanceof GroupWizardBuilder) - && (modelObject.getInnerObject() instanceof GroupTO) - && (formLayoutInfo instanceof GroupFormLayoutInfo)) { + if (this instanceof GroupWizardBuilder + && modelObject instanceof final GroupWrapper gw + && formLayoutInfo instanceof final GroupFormLayoutInfo groupFormLayoutInfo) { - GroupFormLayoutInfo groupFormLayoutInfo = GroupFormLayoutInfo.class.cast(formLayoutInfo); if (groupFormLayoutInfo.isOwnership()) { - wizardModel.add(new Ownership(GroupWrapper.class.cast(modelObject), pageRef)); + wizardModel.add(new Ownership(gw, pageRef)); } if (groupFormLayoutInfo.isDynamicMemberships()) { - wizardModel.add(new DynamicMemberships(GroupWrapper.class.cast(modelObject), pageRef)); + wizardModel.add(new DynamicMemberships(gw, pageRef)); } } @@ -141,10 +140,10 @@ public PageReference getPageReference() { } // role panel step (just available for users) - if ((this instanceof UserWizardBuilder) + if (this instanceof UserWizardBuilder && modelObject instanceof final UserWrapper userWrapper - && formLayoutInfo instanceof UserFormLayoutInfo - && UserFormLayoutInfo.class.cast(formLayoutInfo).isRoles()) { + && formLayoutInfo instanceof final UserFormLayoutInfo ufli + && ufli.isRoles()) { wizardModel.add(new Roles(userWrapper)); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java index 2cf541b1c02..478d1d1fae9 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java @@ -92,14 +92,14 @@ public Details( @Override protected Iterator getChoices(final String input) { - return (pageRef.getPage() instanceof Realms - ? getRealmsFromLinks(Realms.class.cast(pageRef.getPage()).getRealmChoicePanel().getLinks()) + return (pageRef.getPage() instanceof Realms realms + ? getRealmsFromLinks(realms.getRealmChoicePanel().getLinks()) : (fullRealmsTree ? realmRestClient.search(RealmsUtils.buildBaseQuery()) : realmRestClient.search(RealmsUtils.buildKeywordQuery(input))).getResult()). stream().map(RealmTO::getFullPath). - filter(fullPath -> authRealms.stream().anyMatch( - authRealm -> fullPath.startsWith(authRealm))).iterator(); + filter(fullPath -> authRealms.stream().anyMatch( + authRealm -> fullPath.startsWith(authRealm))).iterator(); } }; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupTemplateWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupTemplateWizardBuilder.java index 64cb87af12c..12036a94c93 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupTemplateWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupTemplateWizardBuilder.java @@ -52,9 +52,8 @@ public GroupTemplateWizardBuilder( setItem(new GroupWrapper(GroupTO.class.cast(templatable.getTemplates().get(AnyTypeKind.GROUP.name())))); } else { GroupTO groupTO = new GroupTO(); - if (templatable instanceof RealmTO) { - groupTO.setRealm( - String.format("'%s'", RealmsUtils.getFullPath(RealmTO.class.cast(templatable).getFullPath()))); + if (templatable instanceof final RealmTO realmTO) { + groupTO.setRealm(String.format("'%s'", RealmsUtils.getFullPath(realmTO.getFullPath()))); } setItem(new GroupWrapper(groupTO)); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java index 936adcc25cd..83995f27265 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java @@ -90,8 +90,8 @@ public AjaxWizardBuilder> setItem(final AnyWrapper @Override protected Serializable onApplyInternal(final AnyWrapper modelObject) { - GroupTO updated = modelObject instanceof GroupWrapper - ? GroupWrapper.class.cast(modelObject).fillDynamicConditions() + GroupTO updated = modelObject instanceof GroupWrapper gw + ? gw.fillDynamicConditions() : modelObject.getInnerObject(); ProvisioningResult result; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java index 3a65e1d9afd..2d9a8683a50 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java @@ -314,8 +314,8 @@ public String getAjaxIndicatorMarkupId() { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof SearchClausePanel.SearchEvent) { - AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof SearchClausePanel.SearchEvent payload) { + AjaxRequestTarget target = payload.getTarget(); if (Ownership.this.isGroupOwnership.getObject()) { String fiql = SearchUtils.buildFIQL( groupSearchPanel.getModel().getObject(), SyncopeClient.getGroupSearchConditionBuilder()); @@ -325,7 +325,7 @@ public void onEvent(final IEvent event) { userSearchPanel.getModel().getObject(), SyncopeClient.getUserSearchConditionBuilder()); userDirectoryPanel.search(fiql, target); } - } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection itemSelection) { + } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection itemSelection) { AnyTO sel = itemSelection.getSelection(); if (sel == null) { wrapper.getInnerObject().setUserOwner(null); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java index af58c4ec978..bb000382812 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java @@ -61,19 +61,15 @@ public PlainAttrs( super(modelObject, mode, anyTypeClasses, whichPlainAttrs); - if (modelObject.getInnerObject() instanceof UserTO) { - fileKey = UserTO.class.cast(modelObject.getInnerObject()).getUsername(); - } else if (modelObject.getInnerObject() instanceof GroupTO) { - fileKey = GroupTO.class.cast(modelObject.getInnerObject()).getName(); - } else if (modelObject.getInnerObject() instanceof AnyObjectTO) { - fileKey = AnyObjectTO.class.cast(modelObject.getInnerObject()).getName(); - } - - if (modelObject instanceof UserWrapper) { - previousObject = UserWrapper.class.cast(modelObject).getPreviousUserTO(); - } else { - previousObject = null; - } + fileKey = modelObject.getInnerObject() instanceof UserTO userTO + ? userTO.getUsername() + : modelObject.getInnerObject() instanceof GroupTO groupTO + ? groupTO.getName() + : modelObject.getInnerObject() instanceof AnyObjectTO anyObjectTO + ? anyObjectTO.getName() + : null; + + previousObject = modelObject instanceof UserWrapper uw ? uw.getPreviousUserTO() : null; setTitleModel(new ResourceModel("attributes.plain")); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java index 833c1528e54..3d2de131622 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java @@ -87,11 +87,11 @@ public Relationships(final AnyWrapper modelObject, final PageRe super(); add(new Label("title", new ResourceModel("any.relationships"))); - if (modelObject instanceof UserWrapper - && UserWrapper.class.cast(modelObject).getPreviousUserTO() != null + if (modelObject instanceof final UserWrapper uw + && uw.getPreviousUserTO() != null && !ListUtils.isEqualList( - UserWrapper.class.cast(modelObject).getInnerObject().getRelationships(), - UserWrapper.class.cast(modelObject).getPreviousUserTO().getRelationships())) { + uw.getInnerObject().getRelationships(), + uw.getPreviousUserTO().getRelationships())) { add(new LabelInfo("changed", StringUtils.EMPTY)); } else { add(new Label("changed", StringUtils.EMPTY)); @@ -242,24 +242,19 @@ protected void setupFragment(final String anyType) { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof SearchClausePanel.SearchEvent) { - AjaxRequestTarget target = - SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget(); + if (event.getPayload() instanceof final SearchClausePanel.SearchEvent payload) { String fiql = SearchUtils.buildFIQL(anyObjectSearchPanel.getModel().getObject(), SyncopeClient.getAnyObjectSearchConditionBuilder(anyObjectSearchPanel.getAnyType())); - AnyDirectoryPanel.class.cast(anyObjectDirectoryPanel).search(fiql, target); - } else if (event.getPayload() instanceof AnySelectionDirectoryPanel.ItemSelection) { - AjaxRequestTarget target = - AnySelectionDirectoryPanel.ItemSelection.class.cast(event.getPayload()).getTarget(); - - AnyTO right = AnySelectionDirectoryPanel.ItemSelection.class.cast(event.getPayload()).getSelection(); + AnyDirectoryPanel.class.cast(anyObjectDirectoryPanel).search(fiql, payload.getTarget()); + } else if (event.getPayload() instanceof final AnySelectionDirectoryPanel.ItemSelection payload) { + AnyTO right = payload.getSelection(); rel.setOtherEndKey(right.getKey()); rel.setOtherEndName(AnyObjectTO.class.cast(right).getName()); anyTO.getRelationships().add(rel); Relationships.this.addOrReplace(getViewFragment().setRenderBodyOnly(true)); - target.add(Relationships.this); + payload.getTarget().add(Relationships.this); } else { super.onEvent(event); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java index b6f5bc37ec3..271a370e238 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java @@ -66,9 +66,8 @@ public UserTemplateWizardBuilder( setItem(new UserWrapper(UserTO.class.cast(templatable.getTemplates().get(AnyTypeKind.USER.name())))); } else { UserTO userTO = new UserTO(); - if (templatable instanceof RealmTO) { - userTO.setRealm( - String.format("'%s'", RealmsUtils.getFullPath(RealmTO.class.cast(templatable).getFullPath()))); + if (templatable instanceof final RealmTO realmTO) { + userTO.setRealm(String.format("'%s'", RealmsUtils.getFullPath(realmTO.getFullPath()))); } setItem(new UserWrapper(userTO)); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java index b91a95f60a4..6230f129a0e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java @@ -74,8 +74,8 @@ protected Serializable onApplyInternal(final AnyWrapper modelObject) { if (inner.getKey() == null) { UserCR req = new UserCR(); EntityTOUtils.toAnyCR(inner, req); - req.setStorePassword(modelObject instanceof UserWrapper - ? UserWrapper.class.cast(modelObject).isStorePasswordInSyncope() + req.setStorePassword(modelObject instanceof final UserWrapper uw + ? uw.isStorePasswordInSyncope() : StringUtils.isNotBlank(inner.getPassword())); result = userRestClient.create(req); diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Login.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Login.html index 2b8716e52eb..10fa864d45a 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Login.html +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Login.html @@ -59,7 +59,6 @@ -