This is an automated email from the ASF dual-hosted git repository. ilgrosso pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 3be90d61a89842e6991f63d0f96c2f3421a126bf Author: Francesco Chicchiriccò <[email protected]> AuthorDate: Tue Apr 18 13:56:44 2023 +0200 [SYNCOPE-1752] Refactoring Realms storage and management (#442) --- .../clientapps/ClientAppModalPanelBuilder.java | 16 +- .../client/console/status/ReconTaskPanel.java | 19 ++- .../wizards/resources/ConnectorDetailsPanel.java | 19 ++- .../syncope/client/console/ConsoleProperties.java | 14 +- .../client/console/SyncopeConsoleSession.java | 2 +- .../client/console/SyncopeWebApplication.java | 15 ++ .../client/console/commons/RealmsUtils.java | 32 +--- .../syncope/client/console/pages/BasePage.java | 2 +- .../client/console/panels/RealmChoicePanel.java | 78 +++++----- .../client/console/rest/RealmRestClient.java | 4 - .../console/tasks/SchedTaskWizardBuilder.java | 23 +-- .../client/console/wizards/any/Details.java | 30 ++-- .../console/wizards/role/RoleWizardBuilder.java | 48 ++++-- .../console/src/main/resources/console.properties | 2 + .../client/enduser/panels/any/UserDetails.java | 20 +-- .../client/enduser/rest/AbstractAnyRestClient.java | 1 - .../client/enduser/rest/GroupRestClient.java | 2 +- .../client/enduser/rest/RealmRestClient.java | 36 ----- .../client/enduser/rest/SchemaRestClient.java | 2 +- .../enduser/rest/SecurityQuestionRestClient.java | 1 - .../common/lib/types/IdRepoEntitlement.java | 2 +- .../syncope/common/rest/api/beans/RealmQuery.java | 18 +-- .../common/rest/api/service/RealmService.java | 19 +-- .../src/main/resources/defaultContent.xml | 2 +- .../org/apache/syncope/core/logic/RealmLogic.java | 73 +++------ .../core/rest/cxf/service/RealmServiceImpl.java | 14 +- .../syncope/core/persistence/api/dao/RealmDAO.java | 10 +- .../resources/domains/jpa-json/MasterContent.xml | 4 +- .../src/main/resources/myjson/indexes.xml | 3 + .../src/main/resources/ojson/indexes.xml | 3 + .../src/main/resources/pgjsonb/indexes.xml | 4 + .../src/test/resources/domains/MasterContent.xml | 14 +- .../jpa/content/XMLContentExporter.java | 10 +- .../core/persistence/jpa/dao/JPAAnySearchDAO.java | 18 +-- .../core/persistence/jpa/dao/JPARealmDAO.java | 171 +++++++++++++-------- .../core/persistence/jpa/dao/JPATaskDAO.java | 2 +- .../core/persistence/jpa/entity/JPARealm.java | 14 +- .../src/main/resources/domains/MasterContent.xml | 4 +- .../persistence-jpa/src/main/resources/indexes.xml | 3 + .../src/main/resources/oracle_indexes.xml | 3 + .../persistence/jpa/inner/MultitenancyTest.java | 6 +- .../core/persistence/jpa/inner/RealmTest.java | 10 +- .../jpa/outer/XMLContentExporterTest.java | 6 +- .../src/test/resources/domains/MasterContent.xml | 14 +- .../src/test/resources/domains/TwoContent.xml | 4 +- .../provisioning/java/pushpull/InboundMatcher.java | 5 +- .../java/pushpull/PushJobDelegate.java | 3 +- .../client/ElasticsearchClientContext.java | 2 +- .../jpa/dao/ElasticsearchAnySearchDAO.java | 2 +- .../jpa/dao/ElasticsearchAnySearchDAOTest.java | 3 +- .../syncope/fit/core/reference/TestCommand.java | 3 +- .../org/apache/syncope/fit/AbstractITCase.java | 4 +- .../org/apache/syncope/fit/core/CommandITCase.java | 4 +- .../org/apache/syncope/fit/core/MacroITCase.java | 4 +- .../org/apache/syncope/fit/core/RealmITCase.java | 89 ++++------- .../org/apache/syncope/fit/core/SearchITCase.java | 9 +- pom.xml | 16 +- .../reference-guide/concepts/entitlements.adoc | 4 +- .../asciidoc/reference-guide/concepts/roles.adoc | 4 +- 59 files changed, 466 insertions(+), 483 deletions(-) diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java index 36f1620a89..a80a05ec5b 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java @@ -29,6 +29,7 @@ import java.util.stream.Stream; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; +import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.panels.AbstractModalPanel; import org.apache.syncope.client.console.rest.ClientAppRestClient; @@ -49,7 +50,6 @@ import org.apache.syncope.client.ui.commons.pages.BaseWebPage; import org.apache.syncope.client.ui.commons.panels.WizardModalPanel; import org.apache.syncope.client.ui.commons.wizards.AbstractModalPanelBuilder; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.policy.PolicyTO; import org.apache.syncope.common.lib.to.ClientAppTO; import org.apache.syncope.common.lib.to.RealmTO; @@ -154,10 +154,10 @@ public class ClientAppModalPanelBuilder<T extends ClientAppTO> extends AbstractM List<Component> fields = new ArrayList<>(); - boolean isSearchEnabled = RealmsUtils.isSearchEnabled(); + boolean fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); AutoCompleteSettings settings = new AutoCompleteSettings(); - settings.setShowCompleteListOnFocusGain(!isSearchEnabled); - settings.setShowListOnEmptyInput(!isSearchEnabled); + settings.setShowCompleteListOnFocusGain(fullRealmsTree); + settings.setShowListOnEmptyInput(fullRealmsTree); AjaxSearchFieldPanel realm = new AjaxSearchFieldPanel( "field", "realm", new PropertyModel<>(clientAppTO, "realm"), settings) { @@ -165,11 +165,9 @@ public class ClientAppModalPanelBuilder<T extends ClientAppTO> extends AbstractM @Override protected Iterator<String> getChoices(final String input) { - return (isSearchEnabled - ? RealmRestClient.search(RealmsUtils.buildQuery(input)).getResult() - : RealmRestClient.list(SyncopeConstants.ROOT_REALM)). - stream().filter(realm -> SyncopeConsoleSession.get().getAuthRealms().stream(). - anyMatch(authRealm -> realm.getFullPath().startsWith(authRealm))). + return RealmRestClient.search(fullRealmsTree + ? RealmsUtils.buildRootQuery() + : RealmsUtils.buildKeywordQuery(input)).getResult().stream(). map(RealmTO::getFullPath).collect(Collectors.toList()).iterator(); } }; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java index 1b7e76f473..07b33950ff 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java @@ -18,12 +18,12 @@ */ package org.apache.syncope.client.console.status; -import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.console.SyncopeConsoleSession; +import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.pages.BasePage; import org.apache.syncope.client.console.panels.MultilevelPanel; @@ -72,7 +72,7 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel { @Override protected List<String> load() { return ImplementationRestClient.list(IdMImplementationType.PULL_ACTIONS).stream(). - map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); + map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); } }; @@ -83,7 +83,7 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel { @Override protected List<String> load() { return ImplementationRestClient.list(IdMImplementationType.PUSH_ACTIONS).stream(). - map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); + map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); } }; @@ -116,10 +116,10 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel { form.add(new Label("realm", "")); form.add(new Label("remediation", "")); } else { - boolean isSearchEnabled = RealmsUtils.isSearchEnabled(); + boolean fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); AutoCompleteSettings settings = new AutoCompleteSettings(); - settings.setShowCompleteListOnFocusGain(!isSearchEnabled); - settings.setShowListOnEmptyInput(!isSearchEnabled); + settings.setShowCompleteListOnFocusGain(fullRealmsTree); + settings.setShowListOnEmptyInput(fullRealmsTree); AjaxSearchFieldPanel realm = new AjaxSearchFieldPanel( "realm", "destinationRealm", new PropertyModel<>(taskTO, "destinationRealm"), settings) { @@ -129,11 +129,10 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel { @Override protected Iterator<String> getChoices(final String input) { return (RealmsUtils.checkInput(input) - ? (isSearchEnabled - ? RealmRestClient.search(RealmsUtils.buildQuery(input)).getResult() - : RealmRestClient.list(SyncopeConstants.ROOT_REALM)) + ? (RealmRestClient.search(fullRealmsTree + ? RealmsUtils.buildRootQuery() + : RealmsUtils.buildKeywordQuery(input)).getResult()) : List.<RealmTO>of()).stream(). - sorted(Comparator.comparing(RealmTO::getName)). map(RealmTO::getFullPath).collect(Collectors.toList()).iterator(); } }; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java index f769716a09..f0e05c8c6b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.client.console.SyncopeConsoleSession; +import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchFieldPanel; @@ -32,7 +32,6 @@ import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponent import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.ConnIdBundle; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ConnPoolConfTO; @@ -50,11 +49,11 @@ public class ConnectorDetailsPanel extends WizardStep { super(); setOutputMarkupId(true); - boolean isSearchEnabled = RealmsUtils.isSearchEnabled(); + boolean fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); AutoCompleteSettings settings = new AutoCompleteSettings(); - settings.setShowCompleteListOnFocusGain(!isSearchEnabled); - settings.setShowListOnEmptyInput(!isSearchEnabled); + settings.setShowCompleteListOnFocusGain(fullRealmsTree); + settings.setShowListOnEmptyInput(fullRealmsTree); AjaxSearchFieldPanel realm = new AjaxSearchFieldPanel( "adminRealm", "adminRealm", new PropertyModel<>(connInstanceTO, "adminRealm"), settings) { @@ -63,11 +62,11 @@ public class ConnectorDetailsPanel extends WizardStep { @Override protected Iterator<String> getChoices(final String input) { - return (isSearchEnabled - ? RealmRestClient.search(RealmsUtils.buildQuery(input)).getResult() - : RealmRestClient.list(SyncopeConstants.ROOT_REALM)). - stream().filter(realm -> SyncopeConsoleSession.get().getAuthRealms().stream(). - anyMatch(authRealm -> realm.getFullPath().startsWith(authRealm))). + return (RealmsUtils.checkInput(input) + ? (RealmRestClient.search(fullRealmsTree + ? RealmsUtils.buildRootQuery() + : RealmsUtils.buildKeywordQuery(input)).getResult()) + : List.<RealmTO>of()).stream(). map(RealmTO::getFullPath).collect(Collectors.toList()).iterator(); } }; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java index 3594c84be6..14a8a1ed0a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java @@ -72,6 +72,8 @@ public class ConsoleProperties extends CommonUIProperties { private String defaultAnyPanelClass = AnyPanel.class.getName(); + private int realmsFullTreeThreshold = 20; + private final Topology topology = new Topology(); @Override @@ -84,6 +86,10 @@ public class ConsoleProperties extends CommonUIProperties { this.adminUser = adminUser; } + public Map<String, Class<? extends BasePage>> getPage() { + return page; + } + public String getDefaultAnyPanelClass() { return defaultAnyPanelClass; } @@ -92,8 +98,12 @@ public class ConsoleProperties extends CommonUIProperties { this.defaultAnyPanelClass = defaultAnyPanelClass; } - public Map<String, Class<? extends BasePage>> getPage() { - return page; + public int getRealmsFullTreeThreshold() { + return realmsFullTreeThreshold; + } + + public void setRealmsFullTreeThreshold(final int realmsFullTreeThreshold) { + this.realmsFullTreeThreshold = realmsFullTreeThreshold; } public Topology getTopology() { 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 a2c79b82dd..a80fa45cd4 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 @@ -286,7 +286,7 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba } public List<String> getSearchableRealms() { - Set<String> roots = auth.get(IdRepoEntitlement.REALM_LIST); + Set<String> roots = auth.get(IdRepoEntitlement.REALM_SEARCH); return CollectionUtils.isEmpty(roots) ? List.of() : roots.stream().sorted().collect(Collectors.toList()); 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 3da6a87661..a5de408b57 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 @@ -18,6 +18,7 @@ */ package org.apache.syncope.client.console; + import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication; import de.agilecoders.wicket.core.Bootstrap; import de.agilecoders.wicket.core.settings.BootstrapSettings; @@ -33,6 +34,7 @@ import org.apache.syncope.client.console.commons.AnyWizardBuilderAdditionalSteps import org.apache.syncope.client.console.commons.ExternalResourceProvider; import org.apache.syncope.client.console.commons.ImplementationInfoProvider; import org.apache.syncope.client.console.commons.PolicyTabProvider; +import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.commons.StatusProvider; import org.apache.syncope.client.console.commons.VirSchemaDetailsPanelProvider; import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup; @@ -40,6 +42,7 @@ import org.apache.syncope.client.console.pages.BasePage; import org.apache.syncope.client.console.pages.Dashboard; import org.apache.syncope.client.console.pages.Login; import org.apache.syncope.client.console.pages.MustChangePassword; +import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.client.console.wizards.any.UserFormFinalizer; import org.apache.syncope.client.lib.AnonymousAuthenticationHandler; import org.apache.syncope.client.lib.SyncopeClient; @@ -51,6 +54,7 @@ import org.apache.syncope.client.ui.commons.themes.AdminLTE; import org.apache.syncope.client.ui.commons.wizards.AjaxWizard; import org.apache.syncope.common.keymaster.client.api.ServiceOps; 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.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; @@ -303,6 +307,17 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication { return props.getMaxUploadFileSizeMB(); } + public boolean fullRealmsTree() { + if (props.getRealmsFullTreeThreshold() <= 0) { + return false; + } + + RealmQuery query = RealmsUtils.buildRootQuery(); + query.setPage(1); + query.setSize(0); + return RealmRestClient.search(query).getTotalCount() < props.getRealmsFullTreeThreshold(); + } + public ExternalResourceProvider getResourceProvider() { return resourceProvider; } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java index 1553337c36..375ec10b44 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java @@ -18,21 +18,12 @@ */ package org.apache.syncope.client.console.commons; -import java.util.List; import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.client.console.SyncopeConsoleSession; -import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.rest.api.beans.RealmQuery; public final class RealmsUtils { - public static final int REALMS_VIEW_SIZE = 20; - - private RealmsUtils() { - // private constructor for static utility class - } - public static String getFullPath(final String fullpath) { String display = fullpath; if (display.indexOf('@') != -1) { @@ -41,26 +32,19 @@ public final class RealmsUtils { return display; } - public static boolean isSearchEnabled() { - return isSearchEnabled(SyncopeConsoleSession.get().getAuthRealms()); + public static boolean checkInput(final String input) { + return StringUtils.isNotBlank(input) && !"*".equals(input); } - public static boolean isSearchEnabled(final List<String> realms) { - return realms.isEmpty() - ? false - : RealmRestClient.search( - new RealmQuery.Builder().keyword( - realms.contains(SyncopeConstants.ROOT_REALM) - ? SyncopeConstants.ROOT_REALM - : realms.get(0)).build()). - getTotalCount() > REALMS_VIEW_SIZE; + public static RealmQuery buildKeywordQuery(final String input) { + return new RealmQuery.Builder().keyword(input.contains("*") ? input : "*" + input + "*").build(); } - public static boolean checkInput(final String input) { - return StringUtils.isNotBlank(input) && !"*".equals(input); + public static RealmQuery buildRootQuery() { + return new RealmQuery.Builder().base(SyncopeConstants.ROOT_REALM).build(); } - public static RealmQuery buildQuery(final String input) { - return new RealmQuery.Builder().keyword(input.contains("*") ? input : "*" + input + "*").build(); + private RealmsUtils() { + // private constructor for static utility class } } 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 b60d9c7aa5..9a27bef096 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 @@ -149,7 +149,7 @@ public class BasePage extends BaseWebPage { body.add(liContainer); BookmarkablePageLink<? extends BasePage> link = BookmarkablePageLinkBuilder.build("realms", Realms.class); - MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, IdRepoEntitlement.REALM_LIST); + MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, IdRepoEntitlement.REALM_SEARCH); liContainer.add(link); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java index 6e03afccd6..d36cb813a2 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java @@ -38,6 +38,7 @@ import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.SyncopeConsoleSession; +import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.client.console.wicket.markup.html.WebMarkupContainerNoVeil; @@ -47,6 +48,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.DynRealmTO; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; @@ -92,16 +94,16 @@ public class RealmChoicePanel extends Panel { protected List<RealmTO> realmsChoices; - protected final boolean isSearchEnabled; + protected final boolean fullRealmsTree; protected final ListView<String> breadcrumb; - public RealmChoicePanel(final String id, final String initialRealm, final PageReference pageRef) { + public RealmChoicePanel(final String id, final String base, final PageReference pageRef) { super(id); this.pageRef = pageRef; tree = new HashMap<>(); - isSearchEnabled = RealmsUtils.isSearchEnabled(SyncopeConsoleSession.get().getSearchableRealms()); + fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); realmTree = new LoadableDetachableModel<>() { @@ -111,13 +113,13 @@ public class RealmChoicePanel extends Panel { protected List<Pair<String, RealmTO>> load() { Map<String, Pair<RealmTO, List<RealmTO>>> map = reloadRealmParentMap(); Stream<Pair<String, RealmTO>> full; - if (isSearchEnabled) { - full = map.entrySet().stream(). - map(el -> Pair.of(el.getKey(), el.getValue().getLeft())); - } else { + if (fullRealmsTree) { full = map.entrySet().stream(). map(el -> Pair.of(el.getValue().getLeft().getFullPath(), el.getValue().getKey())). sorted(Comparator.comparing(Pair::getLeft)); + } else { + full = map.entrySet().stream(). + map(el -> Pair.of(el.getKey(), el.getValue().getLeft())); } return full.filter(realm -> SyncopeConsoleSession.get().getSearchableRealms().stream().anyMatch( availableRealm -> realm.getValue().getFullPath().startsWith(availableRealm))). @@ -147,11 +149,11 @@ public class RealmChoicePanel extends Panel { } }; - RealmTO realm = SyncopeConsoleSession.get().getRootRealm(initialRealm).map(rootRealm -> { + RealmTO realm = SyncopeConsoleSession.get().getRootRealm(base).map(rootRealm -> { String rootRealmName = StringUtils.substringAfterLast(rootRealm, "/"); List<RealmTO> realmTOs = RealmRestClient.search( - RealmsUtils.buildQuery(SyncopeConstants.ROOT_REALM.equals(rootRealm) + RealmsUtils.buildKeywordQuery(SyncopeConstants.ROOT_REALM.equals(rootRealm) ? SyncopeConstants.ROOT_REALM : rootRealmName)).getResult(); return realmTOs.stream(). @@ -187,8 +189,8 @@ public class RealmChoicePanel extends Panel { @Override public void onClick(final AjaxRequestTarget target) { - RealmRestClient.list(item.getModelObject()).stream(). - filter(r -> item.getModelObject().equals(r.getFullPath())). + RealmRestClient.search( + new RealmQuery.Builder().base(item.getModelObject()).build()).getResult().stream(). findFirst().ifPresent(t -> chooseRealm(t, target)); } }; @@ -202,7 +204,7 @@ public class RealmChoicePanel extends Panel { container.addOrReplace(breadcrumb.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); setBreadcrumb(model.getObject()); - reloadRealmTree(); + reloadRealmsTree(); } protected void setBreadcrumb(final RealmTO realm) { @@ -232,8 +234,28 @@ public class RealmChoicePanel extends Panel { send(pageRef.getPage(), Broadcast.EXACT, new ChosenRealm<>(realm, target)); } - public void reloadRealmTree() { - if (isSearchEnabled) { + public void reloadRealmsTree() { + if (fullRealmsTree) { + DropDownButton realms = new DropDownButton( + "realms", new ResourceModel("select", ""), new Model<>(FontAwesome5IconType.folder_open_r)) { + + private static final long serialVersionUID = -5560086780455361131L; + + @Override + protected List<AbstractLink> newSubMenuButtons(final String buttonMarkupId) { + buildRealmLinks(); + return RealmChoicePanel.this.links; + } + }; + realms.setOutputMarkupId(true); + realms.setAlignment(DropDownAlignmentBehavior.Alignment.RIGHT); + realms.setType(Buttons.Type.Menu); + + MetaDataRoleAuthorizationStrategy.authorize(realms, ENABLE, IdRepoEntitlement.REALM_SEARCH); + Fragment fragment = new Fragment("realmsFragment", "realmsListFragment", container); + fragment.addOrReplace(realms); + container.addOrReplace(fragment); + } else { realmsChoices = buildRealmChoices(); AutoCompleteSettings settings = new AutoCompleteSettings(); settings.setShowCompleteListOnFocusGain(false); @@ -294,26 +316,6 @@ public class RealmChoicePanel extends Panel { Fragment fragment = new Fragment("realmsFragment", "realmsSearchFragment", container); fragment.addOrReplace(searchRealms); container.addOrReplace(fragment); - } else { - DropDownButton realms = new DropDownButton( - "realms", new ResourceModel("select", ""), new Model<>(FontAwesome5IconType.folder_open_r)) { - - private static final long serialVersionUID = -5560086780455361131L; - - @Override - protected List<AbstractLink> newSubMenuButtons(final String buttonMarkupId) { - buildRealmLinks(); - return RealmChoicePanel.this.links; - } - }; - realms.setOutputMarkupId(true); - realms.setAlignment(DropDownAlignmentBehavior.Alignment.RIGHT); - realms.setType(Buttons.Type.Menu); - - MetaDataRoleAuthorizationStrategy.authorize(realms, ENABLE, IdRepoEntitlement.REALM_LIST); - Fragment fragment = new Fragment("realmsFragment", "realmsListFragment", container); - fragment.addOrReplace(realms); - container.addOrReplace(fragment); } } @@ -419,7 +421,7 @@ public class RealmChoicePanel extends Panel { } public final RealmChoicePanel reloadRealmTree(final AjaxRequestTarget target) { - reloadRealmTree(); + reloadRealmsTree(); chooseRealm(model.getObject(), target); target.add(container); return this; @@ -432,9 +434,9 @@ public class RealmChoicePanel extends Panel { } protected Map<String, Pair<RealmTO, List<RealmTO>>> reloadRealmParentMap() { - List<RealmTO> realmsToList = isSearchEnabled - ? RealmRestClient.search(RealmsUtils.buildQuery(searchQuery)).getResult() - : RealmRestClient.list(SyncopeConstants.ROOT_REALM); + List<RealmTO> realmsToList = RealmRestClient.search(fullRealmsTree + ? RealmsUtils.buildRootQuery() + : RealmsUtils.buildKeywordQuery(searchQuery)).getResult(); return reloadRealmParentMap(realmsToList.stream(). sorted(Comparator.comparing(RealmTO::getName)). diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java index 53eed8e86e..73ccaf96a7 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/RealmRestClient.java @@ -40,10 +40,6 @@ public class RealmRestClient extends BaseRestClient { return getService(RealmService.class).search(query); } - public static List<RealmTO> list(final String fullpath) { - return getService(RealmService.class).list(fullpath); - } - public static List<DynRealmTO> listDynRealms() { return getService(DynRealmService.class).list(); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java index 0484743d49..f45cb0c40a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java @@ -71,12 +71,12 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar private CrontabPanel crontabPanel; - private final boolean isSearchEnabled; + private final boolean fullRealmsTree; public SchedTaskWizardBuilder(final TaskType type, final T taskTO, final PageReference pageRef) { super(taskTO, pageRef); this.type = type; - this.isSearchEnabled = RealmsUtils.isSearchEnabled(); + this.fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); } @Override @@ -105,10 +105,11 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar return wizardModel; } - private List<RealmTO> searchRealms(final String realmQuery) { - return isSearchEnabled - ? RealmRestClient.search(RealmsUtils.buildQuery(realmQuery)).getResult() - : RealmRestClient.list(SyncopeConstants.ROOT_REALM); + protected List<String> searchRealms(final String realmQuery) { + return RealmRestClient.search(fullRealmsTree + ? RealmsUtils.buildRootQuery() + : RealmsUtils.buildKeywordQuery(realmQuery)). + getResult().stream().map(RealmTO::getFullPath).collect(Collectors.toList()); } public class Profile extends WizardStep { @@ -154,8 +155,8 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar add(jobDelegate); AutoCompleteSettings settings = new AutoCompleteSettings(); - settings.setShowCompleteListOnFocusGain(!isSearchEnabled); - settings.setShowListOnEmptyInput(!isSearchEnabled); + settings.setShowCompleteListOnFocusGain(fullRealmsTree); + settings.setShowListOnEmptyInput(fullRealmsTree); // ------------------------------ // Only for macro tasks @@ -172,7 +173,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar @Override protected Iterator<String> getChoices(final String input) { return (RealmsUtils.checkInput(input) - ? searchRealms(input).stream().map(RealmTO::getFullPath).collect(Collectors.toList()) + ? searchRealms(input) : List.<String>of()).iterator(); } }; @@ -247,7 +248,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar @Override protected Iterator<String> getChoices(final String input) { return (RealmsUtils.checkInput(input) - ? searchRealms(input).stream().map(RealmTO::getFullPath).collect(Collectors.toList()) + ? searchRealms(input) : List.<String>of()).iterator(); } }; @@ -283,7 +284,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar @Override protected Iterator<String> getChoices(final String input) { return (RealmsUtils.checkInput(input) - ? searchRealms(input).stream().map(RealmTO::getFullPath).collect(Collectors.toList()) + ? searchRealms(input) : List.<String>of()).iterator(); } }; 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 9840b143f1..761d683568 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 @@ -18,11 +18,11 @@ */ package org.apache.syncope.client.console.wizards.any; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import org.apache.syncope.client.console.SyncopeConsoleSession; +import org.apache.syncope.client.console.SyncopeWebApplication; import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.pages.Realms; import org.apache.syncope.client.console.rest.RealmRestClient; @@ -30,7 +30,6 @@ import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchField import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.wicket.Component; @@ -70,10 +69,10 @@ public class Details<T extends AnyTO> extends WizardStep { AjaxTextFieldPanel.class.cast(realm).enableJexlHelp(); fragment = new Fragment("realmsFragment", "realmsTemplateFragment", this); } else { - boolean isSearchEnabled = RealmsUtils.isSearchEnabled(); - final AutoCompleteSettings settings = new AutoCompleteSettings(); - settings.setShowCompleteListOnFocusGain(!isSearchEnabled); - settings.setShowListOnEmptyInput(!isSearchEnabled); + boolean fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); + AutoCompleteSettings settings = new AutoCompleteSettings(); + settings.setShowCompleteListOnFocusGain(fullRealmsTree); + settings.setShowListOnEmptyInput(fullRealmsTree); realm = new AjaxSearchFieldPanel("destinationRealm", "destinationRealm", new PropertyModel<>(inner, "realm"), settings) { @@ -82,11 +81,11 @@ public class Details<T extends AnyTO> extends WizardStep { @Override protected Iterator<String> getChoices(final String input) { - return (isSearchEnabled - ? RealmRestClient.search(RealmsUtils.buildQuery(input)).getResult() - : pageRef.getPage() instanceof Realms + return (pageRef.getPage() instanceof Realms ? getRealmsFromLinks(Realms.class.cast(pageRef.getPage()).getRealmChoicePanel().getLinks()) - : RealmRestClient.list(SyncopeConstants.ROOT_REALM)). + : (fullRealmsTree + ? RealmRestClient.search(RealmsUtils.buildRootQuery()) + : RealmRestClient.search(RealmsUtils.buildKeywordQuery(input))).getResult()). stream().filter(realm -> authRealms.stream().anyMatch( authRealm -> realm.getFullPath().startsWith(authRealm))). map(RealmTO::getFullPath).collect(Collectors.toList()).iterator(); @@ -112,13 +111,10 @@ public class Details<T extends AnyTO> extends WizardStep { } private static List<RealmTO> getRealmsFromLinks(final List<AbstractLink> realmLinks) { - List<RealmTO> realms = new ArrayList<>(); - - realmLinks.stream(). + return realmLinks.stream(). map(Component::getDefaultModelObject). - filter(modelObject -> modelObject instanceof RealmTO). - forEachOrdered(modelObject -> realms.add((RealmTO) modelObject)); - - return realms; + filter(RealmTO.class::isInstance). + map(RealmTO.class::cast). + collect(Collectors.toList()); } } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java index d8e6478ebb..c962261343 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java @@ -20,26 +20,33 @@ package org.apache.syncope.client.console.wizards.role; import java.io.Serializable; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.client.console.SyncopeWebApplication; +import org.apache.syncope.client.console.commons.RealmsUtils; import org.apache.syncope.client.console.panels.search.UserSearchPanel; import org.apache.syncope.client.console.rest.ApplicationRestClient; import org.apache.syncope.client.console.rest.DynRealmRestClient; import org.apache.syncope.client.console.rest.RealmRestClient; import org.apache.syncope.client.console.rest.RoleRestClient; +import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchFieldPanel; +import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel; import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder; import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion; import org.apache.syncope.client.ui.commons.wizards.AjaxWizardBuilder; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.DynRealmTO; import org.apache.syncope.common.lib.to.PrivilegeTO; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.common.lib.to.RoleTO; import org.apache.wicket.PageReference; +import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings; import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; import org.apache.wicket.extensions.wizard.WizardModel; import org.apache.wicket.extensions.wizard.WizardStep; @@ -99,7 +106,7 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> { return wizardModel; } - public class Details extends WizardStep { + protected class Details extends WizardStep { private static final long serialVersionUID = 5514523040031722255L; @@ -129,7 +136,7 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> { } } - public static class Entitlements extends WizardStep { + protected static class Entitlements extends WizardStep { private static final long serialVersionUID = 5514523040031722256L; @@ -155,21 +162,40 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> { } } - public static class Realms extends WizardStep { + protected static class Realms extends WizardStep { private static final long serialVersionUID = 5514523040031722257L; + @SuppressWarnings("unchecked") public Realms(final RoleTO modelObject) { setTitleModel(new ResourceModel("realms")); - add(new AjaxPalettePanel.Builder<>().build("realms", - new PropertyModel<>(modelObject, "realms"), - new ListModel<>(RealmRestClient.list(SyncopeConstants.ROOT_REALM).stream(). - map(RealmTO::getFullPath).collect(Collectors.toList()))). - hideLabel().setOutputMarkupId(true)); + + boolean fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(); + AutoCompleteSettings settings = new AutoCompleteSettings(); + settings.setShowCompleteListOnFocusGain(fullRealmsTree); + settings.setShowListOnEmptyInput(fullRealmsTree); + AbstractFieldPanel<?> realm = new AjaxSearchFieldPanel( + "panel", "realm", new Model<>(), settings) { + + private static final long serialVersionUID = -6390474600233486704L; + + @Override + protected Iterator<String> getChoices(final String input) { + return RealmRestClient.search(fullRealmsTree + ? RealmsUtils.buildRootQuery() + : RealmsUtils.buildKeywordQuery(input)).getResult().stream(). + map(RealmTO::getFullPath).collect(Collectors.toList()).iterator(); + } + }; + add(new MultiFieldPanel.Builder<>( + new PropertyModel<>(modelObject, "realms")).build( + "realms", + "realms", + (FieldPanel) realm).hideLabel()); } } - public static class DynRealms extends WizardStep { + protected static class DynRealms extends WizardStep { private static final long serialVersionUID = 6846234574424462255L; @@ -183,7 +209,7 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> { } } - public static class Privileges extends WizardStep { + protected static class Privileges extends WizardStep { private static final long serialVersionUID = 6896014330702958579L; diff --git a/client/idrepo/console/src/main/resources/console.properties b/client/idrepo/console/src/main/resources/console.properties index 56a39f567f..ca80121a36 100644 --- a/client/idrepo/console/src/main/resources/console.properties +++ b/client/idrepo/console/src/main/resources/console.properties @@ -66,6 +66,8 @@ console.page.topology=org.apache.syncope.client.console.topology.Topology console.default-any-panel-class=org.apache.syncope.client.console.panels.AnyPanel +console.realms-full-tree-threshold=20 + console.topology.corePoolSize=10 console.topology.maxPoolSize=20 console.topology.queueCapacity=50 diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java index d68070e20c..debec15dea 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/UserDetails.java @@ -19,9 +19,8 @@ package org.apache.syncope.client.enduser.panels.any; import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.password.strength.PasswordStrengthBehavior; -import java.util.stream.Collectors; +import java.util.List; import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.client.enduser.rest.RealmRestClient; import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; @@ -29,7 +28,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.SyncopePasswordStrengthConfig; import org.apache.syncope.client.ui.commons.wizards.any.PasswordPanel; import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper; -import org.apache.syncope.common.lib.to.RealmTO; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.UserTO; import org.apache.wicket.PageReference; import org.apache.wicket.markup.html.basic.Label; @@ -41,8 +40,6 @@ public class UserDetails extends Details<UserTO> { private static final long serialVersionUID = 6592027822510220463L; - private final FieldPanel<String> realm; - protected final AjaxTextFieldPanel username; protected final UserTO userTO; @@ -68,12 +65,15 @@ public class UserDetails extends Details<UserTO> { // ------------------------ // Realm // ------------------------ - realm = new AjaxDropDownChoicePanel<>( - "destinationRealm", "destinationRealm", new PropertyModel<>(userTO, "realm"), false); + add(buildDestinationRealm()); + } - ((AjaxDropDownChoicePanel<String>) realm).setChoices( - RealmRestClient.list().stream().map(RealmTO::getFullPath).collect(Collectors.toList())); - add(realm); + protected FieldPanel<String> buildDestinationRealm() { + AjaxDropDownChoicePanel<String> destinationRealm = new AjaxDropDownChoicePanel<>( + "destinationRealm", "destinationRealm", new PropertyModel<>(userTO, "realm"), false); + destinationRealm.setNullValid(false); + destinationRealm.setChoices(List.of(SyncopeConstants.ROOT_REALM)); + return destinationRealm; } protected static class EditUserPasswordPanel extends Panel { diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AbstractAnyRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AbstractAnyRestClient.java index b03a9cb36b..202c9c2ea0 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AbstractAnyRestClient.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/AbstractAnyRestClient.java @@ -32,5 +32,4 @@ public abstract class AbstractAnyRestClient<TO extends AnyTO> extends BaseRestCl public TO read(final String key) { return getService(getAnyServiceClass()).read(key); } - } diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java index 0bf4cae887..4df6b64ce6 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/GroupRestClient.java @@ -33,7 +33,7 @@ import org.apache.syncope.common.rest.api.service.GroupService; import org.apache.syncope.common.rest.api.service.SyncopeService; /** - * Console client for invoking Rest Group's services. + * Enduser client for invoking Rest Group's services. */ public class GroupRestClient extends AbstractAnyRestClient<GroupTO> { diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/RealmRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/RealmRestClient.java deleted file mode 100644 index def9fd03cc..0000000000 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/RealmRestClient.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.enduser.rest; - -import java.util.List; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.to.RealmTO; -import org.apache.syncope.common.rest.api.service.RealmService; - -/** - * Console client for invoking REST Realm's services. - */ -public class RealmRestClient extends BaseRestClient { - - private static final long serialVersionUID = -8549081557283519638L; - - public static List<RealmTO> list() { - return getService(RealmService.class).list(SyncopeConstants.ROOT_REALM); - } -} diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java index 313fcd066e..c598eb540a 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SchemaRestClient.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.rest.api.service.AnyTypeService; import org.apache.syncope.common.rest.api.service.SchemaService; /** - * Console client for invoking rest schema services. + * Enduser client for invoking rest schema services. */ public class SchemaRestClient extends BaseRestClient { diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SecurityQuestionRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SecurityQuestionRestClient.java index bf11aabd51..350f622b06 100644 --- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SecurityQuestionRestClient.java +++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SecurityQuestionRestClient.java @@ -29,5 +29,4 @@ public class SecurityQuestionRestClient extends BaseRestClient { public static List<SecurityQuestionTO> list() { return getService(SecurityQuestionService.class).list(); } - } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java index ed84d42e6d..1fc48fe143 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java @@ -38,7 +38,7 @@ public final class IdRepoEntitlement { public static final String DOMAIN_DELETE = "DOMAIN_DELETE"; - public static final String REALM_LIST = "REALM_LIST"; + public static final String REALM_SEARCH = "REALM_SEARCH"; public static final String REALM_CREATE = "REALM_CREATE"; diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java index a9ffe8d33d..0858b35680 100644 --- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java +++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java @@ -19,31 +19,29 @@ package org.apache.syncope.common.rest.api.beans; import jakarta.ws.rs.QueryParam; -import java.io.Serializable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -public class RealmQuery implements Serializable { +public class RealmQuery extends AbstractQuery { private static final long serialVersionUID = -2278419397595186866L; - public static class Builder { + public static class Builder extends AbstractQuery.Builder<RealmQuery, Builder> { - private final RealmQuery instance = new RealmQuery(); + @Override + protected RealmQuery newInstance() { + return new RealmQuery(); + } public Builder keyword(final String keyword) { - instance.setKeyword(keyword); + getInstance().setKeyword(keyword); return this; } public Builder base(final String base) { - instance.setBase(base); + getInstance().setBase(base); return this; } - - public RealmQuery build() { - return instance; - } } private String keyword; diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RealmService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RealmService.java index 36b5dbe285..bda03ccb4c 100644 --- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RealmService.java +++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RealmService.java @@ -41,7 +41,6 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import java.util.List; import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.ProvisioningResult; import org.apache.syncope.common.lib.to.RealmTO; @@ -59,29 +58,15 @@ import org.apache.syncope.common.rest.api.beans.RealmQuery; public interface RealmService extends JAXRSService { /** - * Returns a list of existing realms matching the given query (not including descendant realms) and the total number - * of descendant realms. + * Returns a paged list of existing realms matching the given query. * * @param query query conditions - * @return list of existing realms matching the given query (not including descendant realms) and the total number - * of descedant realms + * @return paged list of existing realms matching the given query */ @GET - @Path("search") @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) PagedResult<RealmTO> search(@BeanParam RealmQuery query); - /** - * Returns realms rooted at the given path, including descendant realms. - * - * @param fullPath full path of the root realm where to read from - * @return realms rooted at the given path, including descendant realms - */ - @GET - @Path("{fullPath:.*}") - @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) - List<RealmTO> list(@NotNull @PathParam("fullPath") String fullPath); - /** * Creates a new realm under the given path. * diff --git a/common/keymaster/client-api/src/main/resources/defaultContent.xml b/common/keymaster/client-api/src/main/resources/defaultContent.xml index 70c0218f1a..69372e8095 100644 --- a/common/keymaster/client-api/src/main/resources/defaultContent.xml +++ b/common/keymaster/client-api/src/main/resources/defaultContent.xml @@ -18,7 +18,7 @@ specific language governing permissions and limitations under the License. --> <dataset> - <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/> + <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/" fullPath="/"/> <AnyType id="USER" kind="USER"/> <AnyTypeClass id="BaseUser"/> diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java index 5c259913ee..24118f9d67 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java @@ -100,67 +100,42 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> { this.taskExecutor = taskExecutor; } - @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_LIST + "')") + @PreAuthorize("isAuthenticated()") @Transactional(readOnly = true) - public Pair<Integer, List<RealmTO>> search(final String keyword, final String base) { - Realm baseRealm = base == null ? realmDAO.getRoot() : realmDAO.findByFullPath(base); - if (baseRealm == null) { - LOG.error("Could not find realm '" + base + "'"); - - throw new NotFoundException(base); - } + public Pair<Integer, List<RealmTO>> search( + final String keyword, + final String base, + final int page, + final int size) { - Set<String> roots = AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.REALM_LIST).stream(). - filter(auth -> auth.startsWith(baseRealm.getFullPath())).collect(Collectors.toSet()); + Realm baseRealm = Optional.ofNullable(base == null ? realmDAO.getRoot() : realmDAO.findByFullPath(base)). + orElseThrow(() -> new NotFoundException(base)); - Set<Realm> match = realmDAO.findMatching(keyword).stream(). - filter(realm -> roots.stream().anyMatch(root -> realm.getFullPath().startsWith(root))). - collect(Collectors.toSet()); + int count = realmDAO.countDescendants(baseRealm.getFullPath(), keyword); - int descendants = Math.toIntExact( - match.stream().flatMap(realm -> realmDAO.findDescendants(realm).stream()).distinct().count()); + List<Realm> result = realmDAO.findDescendants(baseRealm.getFullPath(), keyword, page, size); return Pair.of( - descendants, - match.stream().map(realm -> binder.getRealmTO(realm, true)). + count, + result.stream().map(realm -> binder.getRealmTO( + realm, + AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.REALM_SEARCH).stream(). + anyMatch(auth -> realm.getFullPath().startsWith(auth)))). sorted(Comparator.comparing(RealmTO::getFullPath)). collect(Collectors.toList())); } - @PreAuthorize("isAuthenticated()") - @Transactional(readOnly = true) - public List<RealmTO> list(final String fullPath) { - Realm realm = realmDAO.findByFullPath(fullPath); - if (realm == null) { - LOG.error("Could not find realm '" + fullPath + '\''); - - throw new NotFoundException(fullPath); - } - - boolean admin = AuthContextUtils.getAuthorizations().keySet().contains(IdRepoEntitlement.REALM_LIST); - return realmDAO.findDescendants(realm).stream(). - map(descendant -> binder.getRealmTO(descendant, admin)).collect(Collectors.toList()); - } - @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_CREATE + "')") public ProvisioningResult<RealmTO> create(final String parentPath, final RealmTO realmTO) { Realm parent; if (StringUtils.isBlank(realmTO.getParent())) { - parent = realmDAO.findByFullPath(parentPath); - if (parent == null) { - LOG.error("Could not find parent realm " + parentPath); - - throw new NotFoundException(parentPath); - } + parent = Optional.ofNullable(realmDAO.findByFullPath(parentPath)). + orElseThrow(() -> new NotFoundException(parentPath)); realmTO.setParent(parent.getFullPath()); } else { - parent = realmDAO.find(realmTO.getParent()); - if (parent == null) { - LOG.error("Could not find parent realm " + realmTO.getParent()); - - throw new NotFoundException(realmTO.getParent()); - } + parent = Optional.ofNullable(realmDAO.find(realmTO.getParent())). + orElseThrow(() -> new NotFoundException(realmTO.getParent())); if (!parent.getFullPath().equals(parentPath)) { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPath); @@ -190,12 +165,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> { @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_UPDATE + "')") public ProvisioningResult<RealmTO> update(final RealmTO realmTO) { - Realm realm = realmDAO.findByFullPath(realmTO.getFullPath()); - if (realm == null) { - LOG.error("Could not find realm '" + realmTO.getFullPath() + '\''); - - throw new NotFoundException(realmTO.getFullPath()); - } + Realm realm = Optional.ofNullable(realmDAO.findByFullPath(realmTO.getFullPath())). + orElseThrow(() -> new NotFoundException(realmTO.getFullPath())); Map<Pair<String, String>, Set<Attribute>> beforeAttrs = propagationManager.prepareAttrs(realm); @@ -219,7 +190,7 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> { @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_DELETE + "')") public ProvisioningResult<RealmTO> delete(final String fullPath) { Realm realm = Optional.ofNullable(realmDAO.findByFullPath(fullPath)). - orElseThrow(() -> new NotFoundException("Realm " + fullPath)); + orElseThrow(() -> new NotFoundException(fullPath)); if (!realmDAO.findChildren(realm).isEmpty()) { throw SyncopeClientException.build(ClientExceptionType.RealmContains); diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java index 2cdbbab006..6cebbee6d2 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java @@ -21,6 +21,7 @@ package org.apache.syncope.core.rest.cxf.service; import jakarta.ws.rs.core.Response; import java.net.URI; import java.util.List; +import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeConstants; @@ -44,17 +45,14 @@ public class RealmServiceImpl extends AbstractService implements RealmService { @Override public PagedResult<RealmTO> search(final RealmQuery query) { - String keyword = query.getKeyword() == null ? null : query.getKeyword().replace('*', '%'); - - Pair<Integer, List<RealmTO>> result = logic.search(keyword, query.getBase()); + Pair<Integer, List<RealmTO>> result = logic.search( + Optional.ofNullable(query.getKeyword()).map(k -> k.replace('*', '%')).orElse(null), + query.getBase(), + query.getPage(), + query.getSize()); return buildPagedResult(result.getRight(), 1, result.getRight().size(), result.getLeft()); } - @Override - public List<RealmTO> list(final String fullPath) { - return logic.list(StringUtils.prependIfMissing(fullPath, SyncopeConstants.ROOT_REALM)); - } - @Override public Response create(final String parentPath, final RealmTO realmTO) { ProvisioningResult<RealmTO> created = diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java index 7a3b5cbaa8..e05eb840b5 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java @@ -41,7 +41,9 @@ public interface RealmDAO extends DAO<Realm> { List<Realm> findByResource(ExternalResource resource); - List<Realm> findMatching(String keyword); + int countDescendants(String base, String keyword); + + List<Realm> findDescendants(String base, String keyword, int page, int itemsPerPage); <T extends Policy> List<Realm> findByPolicy(T policy); @@ -51,13 +53,7 @@ public interface RealmDAO extends DAO<Realm> { List<Realm> findChildren(Realm realm); - List<Realm> findDescendants(Realm realm); - - List<Realm> findAll(); - Realm save(Realm realm); void delete(Realm realm); - - void delete(String key); } diff --git a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml index f8369fb457..56c5c74801 100644 --- a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml +++ b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml @@ -18,7 +18,7 @@ specific language governing permissions and limitations under the License. --> <dataset> - <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/> + <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/" fullPath="/"/> <AnyType id="USER" kind="USER"/> <AnyTypeClass id="BaseUser"/> @@ -104,5 +104,5 @@ we are happy to inform you that the password request was successfully executed f sender="[email protected]" subject="Password Reset successful" template_id="confirmPasswordReset" traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> - <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> + <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_SEARCH","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> </dataset> diff --git a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml index 0fd0c62b4e..64e0d7a675 100644 --- a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml +++ b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml @@ -21,6 +21,9 @@ under the License. <properties> <comment>Additional indexes (in respect to JPA's)</comment> + <entry key="Realm_parent_id">CREATE INDEX Realm_parent_id ON Realm(parent_id)</entry> + <entry key="Realm_fullPath">CREATE INDEX Realm_fullPath ON Realm(fullPath)</entry> + <entry key="SyncopeUser_realm_id">CREATE INDEX SyncopeUser_realm_id ON SyncopeUser(realm_id)</entry> <entry key="SyncopeUser_username">CREATE UNIQUE INDEX SyncopeUser_username ON SyncopeUser(username)</entry> diff --git a/core/persistence-jpa-json/src/main/resources/ojson/indexes.xml b/core/persistence-jpa-json/src/main/resources/ojson/indexes.xml index ae3f571336..b50d063222 100644 --- a/core/persistence-jpa-json/src/main/resources/ojson/indexes.xml +++ b/core/persistence-jpa-json/src/main/resources/ojson/indexes.xml @@ -21,6 +21,9 @@ under the License. <properties> <comment>Additional indexes (in respect to JPA's)</comment> + <entry key="Realm_parent_id">CREATE INDEX Realm_parent_id ON Realm(parent_id)</entry> + <entry key="Realm_fullPath">CREATE INDEX Realm_fullPath ON Realm(fullPath)</entry> + <entry key="SyncopeUser_realm_id">CREATE INDEX SyncopeUser_realm_id ON SyncopeUser(realm_id)</entry> <entry key="SyncopeUser_lower_username">CREATE INDEX SyncopeUser_lower_username ON SyncopeUser(LOWER(username))</entry> diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml b/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml index ee64b6477b..f93e8ac8b7 100644 --- a/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml +++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml @@ -21,6 +21,10 @@ under the License. <properties> <comment>Additional indexes (in respect to JPA's)</comment> + <entry key="Realm_parent_id">CREATE INDEX Realm_parent_id ON Realm(parent_id)</entry> + <entry key="Realm_fullPath">CREATE INDEX Realm_fullPath ON Realm(fullPath)</entry> + <entry key="Realm_fullPath_startsWith">CREATE INDEX Realm_fullPath_startsWith ON Realm USING GIN (to_tsvector('english', fullPath))</entry> + <entry key="SyncopeUser_realm_id">CREATE INDEX SyncopeUser_realm_id ON SyncopeUser(realm_id)</entry> <entry key="SyncopeUser_username">CREATE UNIQUE INDEX SyncopeUser_username ON SyncopeUser(username)</entry> <entry key="SyncopeUser_lower_username">CREATE INDEX SyncopeUser_lower_username ON SyncopeUser(LOWER(username))</entry> diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml index d061c253f4..df3293b84c 100644 --- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml @@ -248,12 +248,14 @@ under the License. <PlainSchema id="location" type="String" anyTypeClass_id="minimal printer" mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"/> - <Realm id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" name="/" passwordPolicy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1"/> - <Realm id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c" name="odd" - parent_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" accountPolicy_id="06e2ed52-6966-44aa-a177-a0ca7434201f"/> - <Realm id="c5b75db1-fce7-470f-b780-3b9934d82a9d" name="even" + <Realm id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" name="/" fullPath="/" + passwordPolicy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1"/> + <Realm id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c" name="odd" fullPath="/odd" + parent_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" + accountPolicy_id="06e2ed52-6966-44aa-a177-a0ca7434201f"/> + <Realm id="c5b75db1-fce7-470f-b780-3b9934d82a9d" name="even" fullPath="/even" parent_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/> - <Realm id="0679e069-7355-4b20-bd11-a5a0a5453c7c" name="two" + <Realm id="0679e069-7355-4b20-bd11-a5a0a5453c7c" name="two" fullPath="/even/two" parent_id="c5b75db1-fce7-470f-b780-3b9934d82a9d" accountPolicy_id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7" passwordPolicy_id="ce93fcda-dc3a-4369-a7b0-a6108c261c85"/> @@ -957,7 +959,7 @@ $$ } logout="0" csrf="1" routeType="PROTECTED" predicates="[{"cond":null,"factory":"METHOD","args":"GET"}]"/> - <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> + <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_SEARCH","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> <AuditConf id="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" active="1"/> diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java index 5786905f32..e8c1af1d12 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java @@ -71,6 +71,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.helpers.IOUtils; import org.apache.openjpa.lib.util.collections.BidiMap; import org.apache.openjpa.lib.util.collections.DualHashBidiMap; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.content.ContentExporter; import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; @@ -400,11 +401,10 @@ public class XMLContentExporter implements ContentExporter { if (tableName.equalsIgnoreCase(JPARealm.TABLE)) { List<Map<String, String>> realmRows = new ArrayList<>(rows); rows.clear(); - realmDAO.findAll().forEach(realm -> realmRows.stream().filter(row -> { - String id = row.get("ID"); - if (id == null) { - id = row.get("id"); - } + realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, -1, -1). + forEach(realm -> realmRows.stream().filter(row -> { + + String id = Optional.ofNullable(row.get("ID")).orElseGet(() -> row.get("id")); return realm.getKey().equals(id); }).findFirst().ifPresent(rows::add)); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java index 1297e0aa15..34ba905097 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java @@ -22,6 +22,7 @@ import jakarta.persistence.Query; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.ArrayUtils; @@ -130,16 +131,15 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO { goRealm -> groupOwners.add(goRealm.getRight()), () -> { if (realmPath.startsWith("/")) { - Realm realm = realmDAO.findByFullPath(realmPath); - if (realm == null) { - SyncopeClientException noRealm = SyncopeClientException.build( - ClientExceptionType.InvalidRealm); + Realm realm = Optional.ofNullable(realmDAO.findByFullPath(realmPath)).orElseThrow(() -> { + SyncopeClientException noRealm = + SyncopeClientException.build(ClientExceptionType.InvalidRealm); noRealm.getElements().add("Invalid realm specified: " + realmPath); - throw noRealm; - } else { - realmKeys.addAll(realmDAO.findDescendants(realm).stream(). - map(Realm::getKey).collect(Collectors.toSet())); - } + return noRealm; + }); + + realmKeys.addAll(realmDAO.findDescendants(realm.getFullPath(), null, -1, -1).stream(). + map(Realm::getKey).collect(Collectors.toSet())); } else { DynRealm dynRealm = dynRealmDAO.find(realmPath); if (dynRealm == null) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java index b21162c908..1a775cded8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.persistence.jpa.dao; import jakarta.persistence.NoResultException; +import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; import java.util.ArrayList; import java.util.List; @@ -52,8 +53,8 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO { @Override public Realm getRoot() { - TypedQuery<Realm> query = entityManager().createQuery( - "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.parent IS NULL", Realm.class); + TypedQuery<Realm> query = entityManager().createQuery("SELECT e FROM " + JPARealm.class.getSimpleName() + " e " + + "WHERE e.parent IS NULL", Realm.class); Realm result = null; try { @@ -82,35 +83,16 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO { throw new MalformedPathException(fullPath); } - Realm root = getRoot(); - if (root == null) { - return null; - } + TypedQuery<Realm> query = entityManager().createQuery("SELECT e FROM " + JPARealm.class.getSimpleName() + " e " + + "WHERE e.fullPath=:fullPath", Realm.class); + query.setParameter("fullPath", fullPath); - Realm current = root; - for (String pathElement : fullPath.substring(1).split("/")) { - current = findChildren(current).stream(). - filter(realm -> pathElement.equals(realm.getName())).findFirst(). - orElse(null); - if (current == null) { - return null; - } + Realm result = null; + try { + result = query.getSingleResult(); + } catch (NoResultException e) { + LOG.debug("Root realm not found", e); } - return current; - } - - private <T extends Policy> List<Realm> findSamePolicyChildren(final Realm realm, final T policy) { - List<Realm> result = new ArrayList<>(); - - findChildren(realm).stream(). - filter(child -> (policy instanceof AccountPolicy - && child.getAccountPolicy() == null || policy.equals(child.getAccountPolicy())) - || (policy instanceof PasswordPolicy - && child.getPasswordPolicy() == null || policy.equals(child.getPasswordPolicy()))). - forEach(child -> { - result.add(child); - result.addAll(findSamePolicyChildren(child, policy)); - }); return result; } @@ -133,15 +115,90 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO { return query.getResultList(); } + protected int setParameter(final List<Object> parameters, final Object parameter) { + parameters.add(parameter); + return parameters.size(); + } + + protected StringBuilder buildDescendantQuery( + final String base, + final String keyword, + final List<Object> parameters) { + + StringBuilder queryString = new StringBuilder("SELECT e FROM "). + append(JPARealm.class.getSimpleName()).append(" e "). + append("WHERE (e.fullPath=?"). + append(setParameter(parameters, base)). + append(" OR e.fullPath LIKE ?"). + append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base + "/%")). + append(')'); + + if (keyword != null) { + queryString.append(" AND LOWER(e.name) LIKE ?").append(setParameter(parameters, keyword)); + } + + return queryString; + } + @Override - public List<Realm> findMatching(final String keyword) { - TypedQuery<Realm> query = entityManager().createQuery("SELECT e FROM " + JPARealm.class.getSimpleName() + " e " - + "WHERE e.name LIKE :keyword", Realm.class); - query.setParameter("keyword", keyword); + public int countDescendants(final String base, final String keyword) { + List<Object> parameters = new ArrayList<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters); + Query query = entityManager().createQuery(StringUtils.replaceOnce( + queryString.toString(), + "SELECT e ", + "SELECT COUNT(e) ")); + + for (int i = 1; i <= parameters.size(); i++) { + query.setParameter(i, parameters.get(i - 1)); + } + + return ((Number) query.getSingleResult()).intValue(); + } + + @Override + public List<Realm> findDescendants( + final String base, + final String keyword, + final int page, + final int itemsPerPage) { + + List<Object> parameters = new ArrayList<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters); + TypedQuery<Realm> query = entityManager().createQuery( + queryString.append(" ORDER BY e.fullPath").toString(), Realm.class); + + for (int i = 1; i <= parameters.size(); i++) { + query.setParameter(i, parameters.get(i - 1)); + } + + query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1)); + + if (itemsPerPage > 0) { + query.setMaxResults(itemsPerPage); + } return query.getResultList(); } + protected <T extends Policy> List<Realm> findSamePolicyChildren(final Realm realm, final T policy) { + List<Realm> result = new ArrayList<>(); + + findChildren(realm).stream(). + filter(child -> (policy instanceof AccountPolicy + && child.getAccountPolicy() == null || policy.equals(child.getAccountPolicy())) + || (policy instanceof PasswordPolicy + && child.getPasswordPolicy() == null || policy.equals(child.getPasswordPolicy()))). + forEach(child -> { + result.add(child); + result.addAll(findSamePolicyChildren(child, policy)); + }); + + return result; + } + @Override public <T extends Policy> List<Realm> findByPolicy(final T policy) { if (policy instanceof PropagationPolicy || policy instanceof ProvisioningPolicy) { @@ -187,7 +244,7 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO { return query.getResultList(); } - private static void findAncestors(final List<Realm> result, final Realm realm) { + protected void findAncestors(final List<Realm> result, final Realm realm) { if (realm.getParent() != null && !result.contains(realm.getParent())) { result.add(realm.getParent()); findAncestors(result, realm.getParent()); @@ -211,35 +268,35 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO { return query.getResultList(); } - private void findDescendants(final List<Realm> result, final Realm realm) { - result.add(realm); - List<Realm> children = findChildren(realm); - if (children != null) { - children.forEach(child -> findDescendants(result, child)); - } - } - - @Override - public List<Realm> findDescendants(final Realm realm) { - List<Realm> result = new ArrayList<>(); - findDescendants(result, realm); - return result; + protected StringBuilder buildDescendantQuery(final String base, final List<Object> parameters) { + return new StringBuilder("SELECT e FROM "). + append(JPARealm.class.getSimpleName()).append(" e "). + append("WHERE e.fullPath=?"). + append(setParameter(parameters, base)). + append(" OR e.fullPath LIKE ?"). + append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base + "/%")). + append(" ORDER BY e.fullPath"); } - @Transactional(readOnly = true) - @Override - public List<Realm> findAll() { - return findDescendants(getRoot()); + protected String buildFullPath(final Realm realm) { + return realm.getParent() == null + ? SyncopeConstants.ROOT_REALM + : StringUtils.appendIfMissing(realm.getParent().getFullPath(), "/") + realm.getName(); } @Override public Realm save(final Realm realm) { + ((JPARealm) realm).setFullPath(buildFullPath(realm)); return entityManager().merge(realm); } @Override public void delete(final Realm realm) { - findDescendants(realm).forEach(toBeDeleted -> { + if (realm == null || realm.getParent() == null) { + return; + } + + findDescendants(realm.getFullPath(), null, -1, -1).forEach(toBeDeleted -> { roleDAO.findByRealm(toBeDeleted).forEach(role -> role.getRealms().remove(toBeDeleted)); toBeDeleted.setParent(null); @@ -247,14 +304,4 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO { entityManager().remove(toBeDeleted); }); } - - @Override - public void delete(final String key) { - Realm realm = find(key); - if (realm == null) { - return; - } - - delete(realm); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java index d6c0d74035..16f61be329 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java @@ -303,7 +303,7 @@ public class JPATaskDAO extends AbstractDAO<Task<?>> implements TaskDAO { String realmKeysArg = AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.TASK_LIST).stream(). map(realmDAO::findByFullPath). filter(Objects::nonNull). - flatMap(r -> realmDAO.findDescendants(r).stream()). + flatMap(r -> realmDAO.findDescendants(r.getFullPath(), null, -1, -1).stream()). map(Realm::getKey). distinct(). map(realmKey -> "?" + setParameter(parameters, realmKey)). diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java index 528bad8331..a3ac8a13ef 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java @@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa.entity; import jakarta.persistence.Cacheable; import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; @@ -34,8 +35,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.IdRepoImplementationType; import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm; import org.apache.syncope.core.persistence.api.entity.AnyType; @@ -73,6 +72,9 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm { @ManyToOne private JPARealm parent; + @Column(nullable = false, unique = true) + private String fullPath; + @ManyToOne(fetch = FetchType.EAGER) private JPAPasswordPolicy passwordPolicy; @@ -125,9 +127,7 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm { @Override public String getFullPath() { - return getParent() == null - ? SyncopeConstants.ROOT_REALM - : StringUtils.appendIfMissing(getParent().getFullPath(), "/") + getName(); + return fullPath; } @Override @@ -151,6 +151,10 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm { this.parent = (JPARealm) parent; } + public void setFullPath(final String fullPath) { + this.fullPath = fullPath; + } + @Override public void setAccountPolicy(final AccountPolicy accountPolicy) { checkType(accountPolicy, JPAAccountPolicy.class); diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml index c0c0981a3a..68ef918311 100644 --- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml @@ -18,7 +18,7 @@ specific language governing permissions and limitations under the License. --> <dataset> - <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/> + <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/" fullPath="/"/> <AnyType id="USER" kind="USER"/> <AnyTypeClass id="BaseUser"/> @@ -104,5 +104,5 @@ we are happy to inform you that the password request was successfully executed f sender="[email protected]" subject="Password Reset successful" template_id="confirmPasswordReset" traceLevel="FAILURES" events='["[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"]'/> - <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> + <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_SEARCH","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> </dataset> diff --git a/core/persistence-jpa/src/main/resources/indexes.xml b/core/persistence-jpa/src/main/resources/indexes.xml index d15d1c2815..7626e7b58e 100644 --- a/core/persistence-jpa/src/main/resources/indexes.xml +++ b/core/persistence-jpa/src/main/resources/indexes.xml @@ -21,6 +21,9 @@ under the License. <properties> <comment>Additional indexes (in respect to JPA's)</comment> + <entry key="Realm_parent_id">CREATE INDEX Realm_parent_id ON Realm(parent_id)</entry> + <entry key="Realm_fullPath">CREATE INDEX Realm_fullPath ON Realm(fullPath)</entry> + <entry key="SyncopeUser_realm_id">CREATE INDEX SyncopeUser_realm_id ON SyncopeUser(realm_id)</entry> <entry key="SyncopeUser_username">CREATE UNIQUE INDEX SyncopeUser_username ON SyncopeUser(username)</entry> diff --git a/core/persistence-jpa/src/main/resources/oracle_indexes.xml b/core/persistence-jpa/src/main/resources/oracle_indexes.xml index 6997204d8c..3d864abf7f 100644 --- a/core/persistence-jpa/src/main/resources/oracle_indexes.xml +++ b/core/persistence-jpa/src/main/resources/oracle_indexes.xml @@ -21,6 +21,9 @@ under the License. <properties> <comment>Additional indexes (in respect to JPA's)</comment> + <entry key="Realm_parent_id">CREATE INDEX Realm_parent_id ON Realm(parent_id)</entry> + <entry key="Realm_fullPath">CREATE INDEX Realm_fullPath ON Realm(fullPath)</entry> + <entry key="SyncopeUser_realm_id">CREATE INDEX SyncopeUser_realm_id ON SyncopeUser(realm_id)</entry> <entry key="SyncopeUser_lower_username">CREATE INDEX SyncopeUser_lower_username ON SyncopeUser(LOWER(username))</entry> diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java index 02138dd4a6..0d62ccf264 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java @@ -83,8 +83,10 @@ public class MultitenancyTest extends AbstractTest { @Test public void readRealm() { - assertEquals(1, realmDAO.findAll().size()); - assertEquals(realmDAO.getRoot(), realmDAO.findAll().get(0)); + assertEquals(1, realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, -1, -1).size()); + assertEquals( + realmDAO.getRoot(), + realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, -1, -1).get(0)); } @Test diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java index 1ae07640d2..2115cf9250 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java @@ -95,7 +95,7 @@ public class RealmTest extends AbstractTest { @Test public void findAll() { - List<Realm> list = realmDAO.findAll(); + List<Realm> list = realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, -1, -1); assertNotNull(list); assertFalse(list.isEmpty()); list.forEach(Assertions::assertNotNull); @@ -161,10 +161,10 @@ public class RealmTest extends AbstractTest { Realm actual = realmDAO.save(realm); assertNotNull(actual); - String id = actual.getKey(); - assertNotNull(realmDAO.find(id)); + actual = realmDAO.find(actual.getKey()); + assertNotNull(actual); - realmDAO.delete(id); - assertNull(realmDAO.find(id)); + realmDAO.delete(actual); + assertNull(realmDAO.find(actual.getKey())); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java index 2bb59eccb8..42919ba697 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java @@ -60,8 +60,8 @@ public class XMLContentExporterTest extends AbstractTest { filter(row -> row.trim().startsWith("<Realm")).collect(Collectors.toList()); assertEquals(4, realms.size()); assertTrue(realms.get(0).contains("name=\"/\"")); - assertTrue(realms.get(1).contains("name=\"odd\"")); - assertTrue(realms.get(2).contains("name=\"even\"")); - assertTrue(realms.get(3).contains("name=\"two\"")); + assertTrue(realms.get(1).contains("name=\"even\"")); + assertTrue(realms.get(2).contains("name=\"two\"")); + assertTrue(realms.get(3).contains("name=\"odd\"")); } } diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml index 61ad9a8925..e153317e47 100644 --- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml @@ -122,12 +122,14 @@ under the License. <AnyTypeClass id="csv"/> - <Realm id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" name="/" passwordPolicy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1"/> - <Realm id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c" name="odd" - parent_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" accountPolicy_id="06e2ed52-6966-44aa-a177-a0ca7434201f"/> - <Realm id="c5b75db1-fce7-470f-b780-3b9934d82a9d" name="even" + <Realm id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" name="/" fullPath="/" + passwordPolicy_id="986d1236-3ac5-4a19-810c-5ab21d79cba1"/> + <Realm id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c" name="odd" fullPath="/odd" + parent_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" + accountPolicy_id="06e2ed52-6966-44aa-a177-a0ca7434201f"/> + <Realm id="c5b75db1-fce7-470f-b780-3b9934d82a9d" name="even" fullPath="/even" parent_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/> - <Realm id="0679e069-7355-4b20-bd11-a5a0a5453c7c" name="two" + <Realm id="0679e069-7355-4b20-bd11-a5a0a5453c7c" name="two" fullPath="/even/two" parent_id="c5b75db1-fce7-470f-b780-3b9934d82a9d" accountPolicy_id="20ab5a8c-4b0c-432c-b957-f7fb9784d9f7" passwordPolicy_id="ce93fcda-dc3a-4369-a7b0-a6108c261c85"/> @@ -1043,7 +1045,7 @@ $$ } logout="0" csrf="1" routeType="PROTECTED" predicates="[{"cond":null,"factory":"METHOD","args":"GET"}]"/> - <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> + <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_SEARCH","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> <AuditConf id="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" active="1"/> diff --git a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml index c0b90e5d37..43c75da1dd 100644 --- a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml @@ -18,7 +18,7 @@ specific language governing permissions and limitations under the License. --> <dataset> - <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/> + <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/" fullPath="/"/> <AnyType id="USER" kind="USER"/> <AnyTypeClass id="BaseUser"/> @@ -100,5 +100,5 @@ we are happy to inform you that the password request was successfully executed f jsonConf='[{"schema":{"name":"synchronizePasswords","displayName":"Enable Password Synchronization","helpMessage":"If true, the connector will synchronize passwords. The Password Capture Plugin needs to be installed for password synchronization to work.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"maintainLdapGroupMembership","displayName":"Maintain LDAP Group Membership" [...] capabilities='["CREATE","UPDATE","DELETE","SEARCH"]'/> - <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_LIST","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> + <SyncopeRole id="GROUP_OWNER" entitlements='["USER_SEARCH","USER_READ","USER_CREATE","USER_UPDATE","USER_DELETE","ANYTYPECLASS_READ","ANYTYPE_LIST","ANYTYPECLASS_LIST","RELATIONSHIPTYPE_LIST","ANYTYPE_READ","REALM_SEARCH","GROUP_SEARCH","GROUP_READ","GROUP_UPDATE","GROUP_DELETE"]'/> </dataset> diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java index f73649b02e..798e6bfb95 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java @@ -27,6 +27,7 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.OrgUnit; import org.apache.syncope.common.lib.to.Provision; @@ -488,9 +489,7 @@ public class InboundMatcher { case "name": if (orgUnit.isIgnoreCaseMatch()) { - String realmName = connObjectKey; - result.addAll(realmDAO.findAll().stream(). - filter(r -> r.getName().equalsIgnoreCase(realmName)).collect(Collectors.toList())); + result.addAll(realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, connObjectKey, -1, -1)); } else { result.addAll(realmDAO.findByName(connObjectKey).stream().collect(Collectors.toList())); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java index f2310fabef..f72667d1ed 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java @@ -206,7 +206,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> i }); // Never push the root realm - List<Realm> realms = realmDAO.findDescendants(profile.getTask().getSourceRealm()).stream(). + List<Realm> realms = realmDAO.findDescendants( + profile.getTask().getSourceRealm().getFullPath(), null, -1, -1).stream(). filter(realm -> realm.getParent() != null).collect(Collectors.toList()); boolean result = true; for (int i = 0; i < realms.size() && result; i++) { diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java index 4f9b3d2af2..499922b80b 100644 --- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java +++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java @@ -72,7 +72,7 @@ public class ElasticsearchClientContext { return new ElasticsearchIndexLoader(indexManager); } - @ConditionalOnMissingBean + @ConditionalOnMissingBean(name = "syncopeElasticsearchHealthContributor") @Bean(name = { "syncopeElasticsearchHealthContributor", "elasticsearchHealthIndicator", "elasticsearchHealthContributor" }) public HealthContributor syncopeElasticsearchHealthContributor(final ElasticsearchClient client) { diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java index 726b8b8047..c1ba3cd8b7 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java +++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java @@ -158,7 +158,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { return noRealm; }); - realmDAO.findDescendants(realm).forEach(descendant -> queries.add( + realmDAO.findDescendants(realm.getFullPath(), null, -1, -1).forEach(descendant -> queries.add( new Query.Builder().term(QueryBuilders.term(). field("realm").value(FieldValue.of(descendant.getKey())).build()). build())); diff --git a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java b/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java index 1adbd0001b..f17e8c5e48 100644 --- a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java +++ b/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java @@ -114,9 +114,10 @@ public class ElasticsearchAnySearchDAOTest { // 1. mock Realm root = mock(Realm.class); when(root.getKey()).thenReturn("rootKey"); + when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); when(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenReturn(root); - when(realmDAO.findDescendants(root)).thenReturn(List.of(root)); + when(realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, -1, -1)).thenReturn(List.of(root)); // 2. test Set<String> adminRealms = Set.of(SyncopeConstants.ROOT_REALM); diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java index 8ad73db82f..043b77b20a 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java @@ -43,7 +43,8 @@ public class TestCommand implements Command<TestCommandArgs> { private AnyObjectLogic anyObjectLogic; private Optional<RealmTO> getRealm(final String fullPath) { - return realmLogic.list(fullPath).stream().filter(realm -> fullPath.equals(realm.getFullPath())).findFirst(); + return realmLogic.search(null, fullPath, -1, -1).getRight().stream(). + filter(realm -> fullPath.equals(realm.getFullPath())).findFirst(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index cd4558f909..622bf6da8b 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -112,6 +112,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.batch.BatchPayloadParser; import org.apache.syncope.common.rest.api.batch.BatchResponseItem; import org.apache.syncope.common.rest.api.beans.AuditQuery; +import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.syncope.common.rest.api.service.AnyObjectService; import org.apache.syncope.common.rest.api.service.AnyTypeClassService; import org.apache.syncope.common.rest.api.service.AnyTypeService; @@ -1003,7 +1004,8 @@ public abstract class AbstractITCase { } protected static Optional<RealmTO> getRealm(final String fullPath) { - return REALM_SERVICE.list(fullPath).stream().filter(realm -> fullPath.equals(realm.getFullPath())).findFirst(); + return REALM_SERVICE.search(new RealmQuery.Builder().base(fullPath).build()).getResult().stream(). + filter(realm -> fullPath.equals(realm.getFullPath())).findFirst(); } @Autowired diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java index a34bb8255a..9b9c755a02 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CommandITCase.java @@ -36,6 +36,7 @@ import org.apache.syncope.common.lib.types.IdRepoImplementationType; import org.apache.syncope.common.lib.types.ImplementationEngine; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.CommandQuery; +import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.syncope.fit.AbstractITCase; import org.apache.syncope.fit.core.reference.TestCommand; import org.apache.syncope.fit.core.reference.TestCommandArgs; @@ -107,7 +108,8 @@ public class CommandITCase extends AbstractITCase { AnyObjectTO printer = ANY_OBJECT_SERVICE.read(args.getPrinterName()); assertNotNull(printer); assertEquals(args.getParentRealm() + "/" + args.getRealmName(), printer.getRealm()); - assertFalse(REALM_SERVICE.list(printer.getRealm()).isEmpty()); + assertFalse(REALM_SERVICE.search( + new RealmQuery.Builder().base(printer.getRealm()).build()).getResult().isEmpty()); } finally { ANY_OBJECT_SERVICE.delete(args.getPrinterName()); REALM_SERVICE.delete(args.getParentRealm() + "/" + args.getRealmName()); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java index 1276a6ef24..c0452b98df 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java @@ -45,6 +45,7 @@ import org.apache.syncope.common.lib.types.ImplementationEngine; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.ExecSpecs; +import org.apache.syncope.common.rest.api.beans.RealmQuery; import org.apache.syncope.common.rest.api.service.TaskService; import org.apache.syncope.fit.AbstractITCase; import org.apache.syncope.fit.core.reference.TestCommand; @@ -134,7 +135,8 @@ public class MacroITCase extends AbstractITCase { AnyObjectTO printer = ANY_OBJECT_SERVICE.read(TCA.getPrinterName()); assertNotNull(printer); assertEquals(TCA.getParentRealm() + "/" + TCA.getRealmName(), printer.getRealm()); - assertFalse(REALM_SERVICE.list(printer.getRealm()).isEmpty()); + assertFalse(REALM_SERVICE.search( + new RealmQuery.Builder().base(printer.getRealm()).build()).getResult().isEmpty()); } @Test diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java index 681cfbd8f6..d6946059bc 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.Response; import java.util.List; @@ -50,10 +51,8 @@ import org.apache.syncope.common.lib.types.ImplementationEngine; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.RealmQuery; -import org.apache.syncope.common.rest.api.service.RealmService; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.fit.AbstractITCase; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class RealmITCase extends AbstractITCase { @@ -64,21 +63,6 @@ public class RealmITCase extends AbstractITCase { assertTrue(match.getResult().stream().allMatch(realm -> realm.getName().contains("o"))); } - @Test - public void list() { - List<RealmTO> realms = REALM_SERVICE.list(SyncopeConstants.ROOT_REALM); - assertNotNull(realms); - assertFalse(realms.isEmpty()); - realms.forEach(Assertions::assertNotNull); - - try { - REALM_SERVICE.list("a name"); - fail("This should not happen"); - } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.InvalidPath, e.getType()); - } - } - @Test public void createUpdate() { RealmTO realm = new RealmTO(); @@ -86,10 +70,8 @@ public class RealmITCase extends AbstractITCase { // 1. create Response response = REALM_SERVICE.create("/even/two", realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - RealmTO actual = actuals[0]; + RealmTO actual = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); assertNotNull(actual.getKey()); assertEquals("last", actual.getName()); assertEquals("/even/two/last", actual.getFullPath()); @@ -120,8 +102,8 @@ public class RealmITCase extends AbstractITCase { assertNotNull(actual); assertEquals("/odd/last", actual.getFullPath()); - assertEquals(1, REALM_SERVICE.list(SyncopeConstants.ROOT_REALM).stream(). - filter(object -> realm.getName().equals(object.getName())).count()); + assertEquals(1, REALM_SERVICE.search(new RealmQuery.Builder().base(SyncopeConstants.ROOT_REALM).build()). + getResult().stream().filter(r -> realm.getName().equals(r.getName())).count()); // 4. create under invalid path try { @@ -148,7 +130,8 @@ public class RealmITCase extends AbstractITCase { Response response = REALM_SERVICE.create("/even/two", realm); assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); - List<RealmTO> realms = REALM_SERVICE.list("/even/two/73~1~19534"); + List<RealmTO> realms = REALM_SERVICE.search(new RealmQuery.Builder(). + base("/even/two/73~1~19534").build()).getResult(); assertEquals(1, realms.size()); assertEquals(realm.getName(), realms.get(0).getName()); } @@ -180,20 +163,16 @@ public class RealmITCase extends AbstractITCase { realm.setName("withPolicy"); response = REALM_SERVICE.create(SyncopeConstants.ROOT_REALM, realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - realm = actuals[0]; + realm = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); String existingAccountPolicy = realm.getAccountPolicy(); realm.setAccountPolicy(policy.getKey()); REALM_SERVICE.update(realm); - actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - RealmTO actual = actuals[0]; + RealmTO actual = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); assertEquals(policy.getKey(), actual.getAccountPolicy()); // 3. remove policy @@ -221,20 +200,16 @@ public class RealmITCase extends AbstractITCase { realm.setName("withAuthPolicy"); Response response = REALM_SERVICE.create(SyncopeConstants.ROOT_REALM, realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - realm = actuals[0]; + realm = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); String existingAuthPolicy = realm.getAuthPolicy(); realm.setAuthPolicy(policy.getKey()); REALM_SERVICE.update(realm); - actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - RealmTO actual = actuals[0]; + RealmTO actual = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); assertEquals(policy.getKey(), actual.getAuthPolicy()); // 3. remove policy @@ -262,20 +237,16 @@ public class RealmITCase extends AbstractITCase { realm.setName("withAccessPolicy"); Response response = REALM_SERVICE.create(SyncopeConstants.ROOT_REALM, realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - realm = actuals[0]; + realm = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); String existingAccessPolicy = realm.getAccessPolicy(); realm.setAccessPolicy(policy.getKey()); REALM_SERVICE.update(realm); - actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - RealmTO actual = actuals[0]; + RealmTO actual = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); assertEquals(policy.getKey(), actual.getAccessPolicy()); // 3. remove policy @@ -304,20 +275,16 @@ public class RealmITCase extends AbstractITCase { realm.setName("withAttrReleasePolicy"); Response response = REALM_SERVICE.create(SyncopeConstants.ROOT_REALM, realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - realm = actuals[0]; + realm = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); String existingAttrReleasePolicy = realm.getAttrReleasePolicy(); realm.setAttrReleasePolicy(policy.getKey()); REALM_SERVICE.update(realm); - actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - RealmTO actual = actuals[0]; + RealmTO actual = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); assertEquals(policy.getKey(), actual.getAttrReleasePolicy()); // 3. remove policy @@ -334,15 +301,13 @@ public class RealmITCase extends AbstractITCase { realm.setName("deletable3"); Response response = REALM_SERVICE.create("/even/two", realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - RealmTO actual = actuals[0]; + RealmTO actual = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); REALM_SERVICE.delete(actual.getFullPath()); try { - REALM_SERVICE.list(actual.getFullPath()); + REALM_SERVICE.search(new RealmQuery.Builder().base(actual.getFullPath()).build()); fail("This should not happen"); } catch (SyncopeClientException e) { assertEquals(ClientExceptionType.NotFound, e.getType()); @@ -421,7 +386,7 @@ public class RealmITCase extends AbstractITCase { @Test public void issueSYNCOPE1472() { // 1. assign twice resource-ldap-orgunit to /odd - RealmTO realmTO = REALM_SERVICE.list("/odd").get(0); + RealmTO realmTO = REALM_SERVICE.search(new RealmQuery.Builder().base("/odd").build()).getResult().get(0); realmTO.getResources().clear(); realmTO.getResources().add("resource-ldap-orgunit"); realmTO.getResources().add("resource-ldap-orgunit"); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java index cd81a2050b..eff056471d 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import java.util.ArrayList; import java.util.List; @@ -55,10 +56,10 @@ import org.apache.syncope.common.lib.to.RoleTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.AnyQuery; import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery; import org.apache.syncope.common.rest.api.service.GroupService; -import org.apache.syncope.common.rest.api.service.RealmService; import org.apache.syncope.common.rest.api.service.RoleService; import org.apache.syncope.fit.AbstractITCase; import org.identityconnectors.framework.common.objects.Name; @@ -816,10 +817,8 @@ public class SearchITCase extends AbstractITCase { // 1. create Realm Response response = REALM_SERVICE.create("/even/two", realm); - RealmTO[] actuals = getObject(response.getLocation(), RealmService.class, RealmTO[].class); - assertNotNull(actuals); - assertTrue(actuals.length > 0); - realm = actuals[0]; + realm = getRealm(response.getHeaderString(RESTHeaders.RESOURCE_KEY)). + orElseThrow(() -> new NotFoundException()); assertNotNull(realm.getKey()); assertEquals("syncope1727", realm.getName()); assertEquals("/even/two/syncope1727", realm.getFullPath()); diff --git a/pom.xml b/pom.xml index 3b2602abcf..3c6c48d054 100644 --- a/pom.xml +++ b/pom.xml @@ -431,7 +431,7 @@ under the License. <elasticsearch.version>8.7.0</elasticsearch.version> <disruptor.version>3.4.4</disruptor.version> - + <commons-jexl.version>3.3</commons-jexl.version> <commons-text.version>1.10.0</commons-text.version> @@ -500,9 +500,9 @@ under the License. <docker.mysql.version>8.0</docker.mysql.version> <docker.mariadb.version>10</docker.mariadb.version> - <jdbc.postgresql.version>42.5.4</jdbc.postgresql.version> + <jdbc.postgresql.version>42.6.0</jdbc.postgresql.version> <jdbc.mysql.version>8.0.32</jdbc.mysql.version> - <jdbc.mariadb.version>3.1.2</jdbc.mariadb.version> + <jdbc.mariadb.version>3.1.3</jdbc.mariadb.version> <jdbc.mssql.version>12.2.0.jre</jdbc.mssql.version> <jdbc.oracle.version>21.9.0.0</jdbc.oracle.version> @@ -659,7 +659,7 @@ under the License. <dependency> <groupId>com.fasterxml.woodstox</groupId> <artifactId>woodstox-core</artifactId> - <version>6.4.0</version> + <version>6.5.0</version> </dependency> <dependency> @@ -1706,7 +1706,7 @@ under the License. <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> - <version>0.8.8</version> + <version>0.8.9</version> </plugin> <plugin> @@ -1996,7 +1996,7 @@ under the License. <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>3.4.1</version> + <version>3.5.0</version> <configuration> <source>${targetJdk}</source> <destDir>apidocs/4.0</destDir> @@ -2060,7 +2060,7 @@ under the License. <plugin> <groupId>org.cyclonedx</groupId> <artifactId>cyclonedx-maven-plugin</artifactId> - <version>2.7.6</version> + <version>2.7.7</version> <executions> <execution> <phase>package</phase> @@ -2133,7 +2133,7 @@ under the License. <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> - <version>2.2.2</version> + <version>2.2.3</version> <inherited>false</inherited> <dependencies> <dependency> diff --git a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc index f6cf1a4561..4986929cb9 100644 --- a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc +++ b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc @@ -31,12 +31,12 @@ https://github.com/apache/syncope/blob/master/core/logic/src/main/java/org/apach endif::[] , the https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#el-common-built-in[`hasRole` expression^] -is used together with one of the standard entitlements to restrict access only to Users owning the `REALM_LIST` +is used together with one of the standard entitlements to restrict access only to Users owning the `REALM_SEARCH` entitlement. [source,java] ---- -@PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_LIST + "')") +@PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_SEARCH + "')") public List<RealmTO> list(final String fullPath) { ---- diff --git a/src/main/asciidoc/reference-guide/concepts/roles.adoc b/src/main/asciidoc/reference-guide/concepts/roles.adoc index 216de6c2e1..83e8cfa313 100644 --- a/src/main/asciidoc/reference-guide/concepts/roles.adoc +++ b/src/main/asciidoc/reference-guide/concepts/roles.adoc @@ -93,7 +93,7 @@ For example, the following entitlements are normally required to be granted for . `RELATIONSHIPTYPE_LIST` . `USER_READ` . `ANYTYPE_READ` -. `REALM_LIST` +. `REALM_SEARCH` . `GROUP_SEARCH` ==== @@ -122,7 +122,7 @@ The actual Entitlements are assigned through the predefined `GROUP_OWNER` Role: . `ANYTYPECLASS_LIST` . `RELATIONSHIPTYPE_LIST` . `ANYTYPE_READ` -. `REALM_LIST` +. `REALM_SEARCH` . `GROUP_SEARCH` . `GROUP_READ` . `GROUP_UPDATE`
