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
The following commit(s) were added to refs/heads/master by this push:
new 2800cb3c82 [SYNCOPE-1949] Allow to search AuthProfile owners (#1296)
2800cb3c82 is described below
commit 2800cb3c82b42ee7dc613b0759038e899ddcdee2
Author: mrcx5 <[email protected]>
AuthorDate: Mon Feb 16 12:20:41 2026 +0100
[SYNCOPE-1949] Allow to search AuthProfile owners (#1296)
---
.../authprofiles/AuthProfileDirectoryPanel.java | 29 ++++++++-
.../console/authprofiles/AuthProfilePanel.java | 70 ++++++++++++++++++++++
.../apache/syncope/client/console/pages/WA.java | 4 +-
.../client/console/rest/AuthProfileRestClient.java | 13 ++--
.../console/authprofiles/AuthProfilePanel.html | 36 +++++++++++
.../common/rest/api/beans/AuthProfileQuery.java | 55 +++++++++++++++++
.../rest/api/service/AuthProfileService.java | 16 ++---
.../syncope/core/logic/AuthProfileLogic.java | 8 +--
.../rest/cxf/service/AuthProfileServiceImpl.java | 7 ++-
.../core/persistence/api/dao/AuthProfileDAO.java | 5 ++
.../persistence/jpa/inner/AuthProfileTest.java | 13 ++++
.../core/persistence/neo4j/PersistenceContext.java | 18 +++++-
.../neo4j/dao/repo/AuthProfileRepo.java | 2 +-
.../neo4j/dao/repo/AuthProfileRepoExt.java} | 11 ++--
.../neo4j/dao/repo/AuthProfileRepoExtImpl.java | 65 ++++++++++++++++++++
.../persistence/neo4j/inner/AuthProfileTest.java | 12 ++++
.../fit/core/wa/GoogleMfaAuthTokenITCase.java | 56 +++++++++++++----
17 files changed, 373 insertions(+), 47 deletions(-)
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java
index ed955350ac..be7597329e 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java
@@ -24,10 +24,13 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Strings;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import
org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel.AuthProfileProvider;
import org.apache.syncope.client.console.commons.AMConstants;
import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.KeywordSearchEvent;
import org.apache.syncope.client.console.panels.DirectoryPanel;
import org.apache.syncope.client.console.panels.ModalDirectoryPanel;
import org.apache.syncope.client.console.rest.AuthProfileRestClient;
@@ -48,6 +51,7 @@ import org.apache.syncope.common.lib.wa.MfaTrustedDevice;
import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import
org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
@@ -62,6 +66,8 @@ public class AuthProfileDirectoryPanel
private static final long serialVersionUID = 2018518567549153364L;
+ private String keyword;
+
private final BaseModal<AuthProfileTO> authProfileModal;
public AuthProfileDirectoryPanel(
@@ -431,6 +437,25 @@ public class AuthProfileDirectoryPanel
return panel;
}
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof KeywordSearchEvent payload) {
+ keyword = payload.getKeyword();
+ if (StringUtils.isNotBlank(keyword)) {
+ if (!Strings.CS.startsWith(keyword, "*")) {
+ keyword = "*" + keyword;
+ }
+ if (!Strings.CS.endsWith(keyword, "*")) {
+ keyword += "*";
+ }
+ }
+
+ updateResultTable(payload.getTarget());
+ } else {
+ super.onEvent(event);
+ }
+ }
+
protected final class AuthProfileProvider extends
DirectoryDataProvider<AuthProfileTO> {
private static final long serialVersionUID = -185944053385660794L;
@@ -443,12 +468,12 @@ public class AuthProfileDirectoryPanel
@Override
public Iterator<AuthProfileTO> iterator(final long first, final long
count) {
int page = ((int) first / paginatorRows);
- return restClient.list((page < 0 ? 0 : page) + 1,
paginatorRows).iterator();
+ return restClient.search(keyword, (page < 0 ? 0 : page) + 1,
paginatorRows).iterator();
}
@Override
public long size() {
- return restClient.count();
+ return restClient.count(keyword);
}
@Override
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.java
new file mode 100644
index 0000000000..f880f64cac
--- /dev/null
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.authprofiles;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.KeywordSearchEvent;
+import org.apache.syncope.client.console.rest.AuthProfileRestClient;
+import
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+
+
+public class AuthProfilePanel extends Panel {
+
+ private static final long serialVersionUID = -4716856239434102405L;
+
+ public AuthProfilePanel(final String id,
+ final AuthProfileRestClient authProfileRestClient,
+ final PageReference pageRef) {
+ super(id);
+
+ Model<String> keywordModel = new Model<>(StringUtils.EMPTY);
+
+ WebMarkupContainer searchBoxContainer = new
WebMarkupContainer("searchBox");
+ add(searchBoxContainer);
+
+ Form<?> form = new Form<>("form");
+ searchBoxContainer.add(form);
+
+ AjaxTextFieldPanel filter = new AjaxTextFieldPanel("filter", "filter",
keywordModel, true);
+
form.add(filter.hideLabel().setOutputMarkupId(true).setRenderBodyOnly(true));
+
+ AjaxButton search = new AjaxButton("search") {
+
+ private static final long serialVersionUID = 8390605330558248736L;
+
+ @Override
+ protected void onSubmit(final AjaxRequestTarget target) {
+ send(AuthProfilePanel.this, Broadcast.DEPTH, new
KeywordSearchEvent(target, keywordModel.getObject()));
+ }
+ };
+ search.setOutputMarkupId(true);
+ form.add(search);
+ form.setDefaultButton(search);
+
+ add(new AuthProfileDirectoryPanel("authProfiles",
authProfileRestClient, pageRef).setOutputMarkupId(true));
+ }
+}
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
index f6a1003ccd..e37d5a2cb9 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
@@ -34,7 +34,7 @@ import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.console.SyncopeWebApplication;
-import
org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel;
+import org.apache.syncope.client.console.authprofiles.AuthProfilePanel;
import org.apache.syncope.client.console.clientapps.ClientApps;
import org.apache.syncope.client.console.panels.AMSessionPanel;
import org.apache.syncope.client.console.panels.AttrRepoDirectoryPanel;
@@ -264,7 +264,7 @@ public class WA extends BasePage {
@Override
public Panel getPanel(final String panelId) {
- return new AuthProfileDirectoryPanel(panelId,
authProfileRestClient, getPageReference());
+ return new AuthProfilePanel(panelId,
authProfileRestClient, getPageReference());
}
});
}
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
index d6801145ed..99f8cb18ce 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
@@ -20,18 +20,23 @@ package org.apache.syncope.client.console.rest;
import java.util.List;
import org.apache.syncope.common.lib.to.AuthProfileTO;
+import org.apache.syncope.common.rest.api.beans.AuthProfileQuery;
import org.apache.syncope.common.rest.api.service.AuthProfileService;
public class AuthProfileRestClient extends BaseRestClient {
private static final long serialVersionUID = -7379778542101161274L;
- public long count() {
- return getService(AuthProfileService.class).list(1, 1).getTotalCount();
+ public long count(final String keyword) {
+ return getService(AuthProfileService.class).
+ search(new
AuthProfileQuery.Builder().page(1).size(0).keyword(keyword).build()).
+ getTotalCount();
}
- public List<AuthProfileTO> list(final int page, final int size) {
- return getService(AuthProfileService.class).list(page,
size).getResult();
+ public List<AuthProfileTO> search(final String keyword, final int page,
final int size) {
+ return getService(AuthProfileService.class).
+ search(new
AuthProfileQuery.Builder().page(page).size(size).keyword(keyword).build()).
+ getResult();
}
public AuthProfileTO read(final String key) {
diff --git
a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.html
b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.html
new file mode 100644
index 0000000000..8d2a5af99e
--- /dev/null
+++
b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfilePanel.html
@@ -0,0 +1,36 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org">
+ <wicket:panel>
+ <div wicket:id="searchBox">
+ <form wicket:id="form">
+ <div class="input-group mb-3">
+ <span wicket:id="filter">[FILTER]</span>
+ <span class="input-group-btn">
+ <button type="button" class="btn btn-default btn-flat"
wicket:id="search">
+ <span class="fas fa-search" aria-hidden="true"></span>
+ </button>
+ </span>
+ </div>
+ </form>
+ </div>
+
+ <div wicket:id="authProfiles"></div>
+ </wicket:panel>
+</html>
diff --git
a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuthProfileQuery.java
b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuthProfileQuery.java
new file mode 100644
index 0000000000..9b43bdb237
--- /dev/null
+++
b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuthProfileQuery.java
@@ -0,0 +1,55 @@
+/*
+ * 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.common.rest.api.beans;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.ws.rs.QueryParam;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
+
+public class AuthProfileQuery extends AbstractQuery {
+
+ private static final long serialVersionUID = -466451202500478654L;
+
+ public static class Builder extends
AbstractQuery.Builder<AuthProfileQuery, Builder> {
+
+ @Override
+ protected AuthProfileQuery newInstance() {
+ return new AuthProfileQuery();
+ }
+
+ public Builder keyword(final String keyword) {
+ getInstance().setKeyword(keyword);
+ return this;
+ }
+ }
+
+ private String keyword;
+
+ @Parameter(name = JAXRSService.PARAM_KEYWORD, description = "keyword to
match", schema =
+ @Schema(implementation = String.class))
+ public String getKeyword() {
+ return keyword;
+ }
+
+ @QueryParam(JAXRSService.PARAM_KEYWORD)
+ public void setKeyword(final String keyword) {
+ this.keyword = keyword;
+ }
+}
diff --git
a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
index 81ba99188c..ed4073529f 100644
---
a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
+++
b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
@@ -27,24 +27,23 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
+import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
-import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.syncope.common.lib.to.AuthProfileTO;
import org.apache.syncope.common.lib.to.PagedResult;
import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AuthProfileQuery;
/**
* REST operations for Auth profiles.
@@ -57,17 +56,14 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
public interface AuthProfileService extends JAXRSService {
/**
- * Returns the paginated list of existing auth profiles.
+ * Returns a paged list of all AuthProfiles.
*
- * @param page search page
- * @param size search page size
- * @return the paginated list of existing auth profiles
+ * @param query query conditions
+ * @return list of all AuthProfiles.
*/
@GET
@Produces({ MediaType.APPLICATION_JSON })
- PagedResult<AuthProfileTO> list(
- @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue("1") int page,
- @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue("25") int size);
+ PagedResult<AuthProfileTO> search(@BeanParam AuthProfileQuery query);
/**
* Returns the auth profile matching the provided if key, if found.
diff --git
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
index e13ccc1caf..a14c3eec84 100644
---
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
+++
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
@@ -48,12 +48,10 @@ public class AuthProfileLogic extends
AbstractAuthProfileLogic {
@PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_LIST + "')")
@Transactional(readOnly = true)
- public Page<AuthProfileTO> list(final Pageable pageable) {
- long count = authProfileDAO.count();
-
- List<AuthProfileTO> result = authProfileDAO.findAll(pageable).
+ public Page<AuthProfileTO> search(final String keyword, final Pageable
pageable) {
+ long count = authProfileDAO.countByOwnerLike(keyword);
+ List<AuthProfileTO> result = authProfileDAO.findByOwnerLike(keyword,
pageable).
stream().map(binder::getAuthProfileTO).toList();
-
return new SyncopePage<>(result, pageable, count);
}
diff --git
a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
index c19e418143..998abb7389 100644
---
a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
+++
b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
@@ -23,10 +23,10 @@ import java.net.URI;
import org.apache.syncope.common.lib.to.AuthProfileTO;
import org.apache.syncope.common.lib.to.PagedResult;
import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AuthProfileQuery;
import org.apache.syncope.common.rest.api.service.AuthProfileService;
import org.apache.syncope.core.logic.AuthProfileLogic;
import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
public class AuthProfileServiceImpl extends AbstractService implements
AuthProfileService {
@@ -37,8 +37,9 @@ public class AuthProfileServiceImpl extends AbstractService
implements AuthProfi
}
@Override
- public PagedResult<AuthProfileTO> list(final int page, final int size) {
- Page<AuthProfileTO> result = logic.list(PageRequest.of(page < 1 ? 0 :
page - 1, size < 1 ? 1 : size));
+ public PagedResult<AuthProfileTO> search(final AuthProfileQuery query) {
+ String keyword = query.getKeyword() == null ? "%" :
query.getKeyword().replace('*', '%');
+ Page<AuthProfileTO> result = logic.search(keyword, pageable(query));
return buildPagedResult(result);
}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
index 89b5c5d563..747bfe2051 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.persistence.api.dao;
+import java.util.List;
import java.util.Optional;
import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
import org.springframework.data.domain.Page;
@@ -28,4 +29,8 @@ public interface AuthProfileDAO extends DAO<AuthProfile> {
Optional<? extends AuthProfile> findByOwner(String owner);
Page<? extends AuthProfile> findAll(Pageable pageable);
+
+ List<? extends AuthProfile> findByOwnerLike(String owner, Pageable
pageable);
+
+ long countByOwnerLike(String owner);
}
diff --git
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
index 104d916e53..47ceea5b06 100644
---
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
+++
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
@@ -192,4 +192,17 @@ public class AuthProfileTest extends AbstractTest {
profile.setGoogleMfaAuthAccounts(List.of(account));
return authProfileDAO.save(profile);
}
+
+ @Test
+ public void findByOwnerLike() {
+ createAuthProfileWithAccount("owner1");
+ createAuthProfileWithAccount("owner2");
+ createAuthProfileWithAccount("test");
+
+ entityManager.flush();
+
+ List<? extends AuthProfile> result =
authProfileDAO.findByOwnerLike("owner%", Pageable.unpaged());
+ assertEquals(2, result.size());
+ assertEquals(2, authProfileDAO.countByOwnerLike("owner%"));
+ }
}
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java
index cc65777e59..ad15dc85f7 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java
@@ -121,6 +121,8 @@ import
org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepo;
import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepoExt;
import
org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepoExtImpl;
import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepo;
+import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepoExt;
+import
org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepoExtImpl;
import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepo;
import
org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExt;
import
org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExtImpl;
@@ -700,8 +702,20 @@ public class PersistenceContext {
@ConditionalOnMissingBean
@Bean
- public AuthProfileDAO authProfileDAO(final SyncopeNeo4jRepositoryFactory
neo4jRepositoryFactory) {
- return neo4jRepositoryFactory.getRepository(AuthProfileRepo.class);
+ public AuthProfileRepoExt authProfileRepoExt(
+ final Neo4jTemplate neo4jTemplate,
+ final Neo4jClient neo4jClient) {
+
+ return new AuthProfileRepoExtImpl(neo4jTemplate, neo4jClient);
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ public AuthProfileDAO authProfileDAO(
+ final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory,
+ final AuthProfileRepoExt authProfileRepoExt) {
+
+ return neo4jRepositoryFactory.getRepository(AuthProfileRepo.class,
authProfileRepoExt);
}
@ConditionalOnMissingBean
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java
index 86ccb54bed..e70ec8ebce 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java
@@ -23,6 +23,6 @@ import
org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthProfile;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface AuthProfileRepo
- extends PagingAndSortingRepository<Neo4jAuthProfile, String>,
AuthProfileDAO {
+ extends PagingAndSortingRepository<Neo4jAuthProfile, String>,
AuthProfileRepoExt, AuthProfileDAO {
}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExt.java
similarity index 75%
copy from
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
copy to
core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExt.java
index 89b5c5d563..77c3ae371c 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExt.java
@@ -16,16 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.persistence.api.dao;
+package org.apache.syncope.core.persistence.neo4j.dao.repo;
-import java.util.Optional;
+import java.util.List;
import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
-import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-public interface AuthProfileDAO extends DAO<AuthProfile> {
+public interface AuthProfileRepoExt {
- Optional<? extends AuthProfile> findByOwner(String owner);
+ List<? extends AuthProfile> findByOwnerLike(String owner, Pageable
pageable);
- Page<? extends AuthProfile> findAll(Pageable pageable);
+ long countByOwnerLike(String owner);
}
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExtImpl.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExtImpl.java
new file mode 100644
index 0000000000..3b3ea1904d
--- /dev/null
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepoExtImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.core.persistence.neo4j.dao.repo;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
+import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO;
+import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthProfile;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.neo4j.core.Neo4jClient;
+import org.springframework.data.neo4j.core.Neo4jTemplate;
+
+
+public class AuthProfileRepoExtImpl extends AbstractDAO implements
AuthProfileRepoExt {
+
+ public AuthProfileRepoExtImpl(
+ final Neo4jTemplate neo4jTemplate,
+ final Neo4jClient neo4jClient) {
+
+ super(neo4jTemplate, neo4jClient);
+ }
+
+ protected StringBuilder query(final String owner) {
+ return new StringBuilder(
+ "MATCH (n:").append(Neo4jAuthProfile.NODE).append(") WHERE ").
+ append("n.owner =~
\"").append(AnyRepoExt.escapeForLikeRegex(owner).replace("%", ".*")).
+ append('"');
+ }
+
+ @Override
+ public List<? extends AuthProfile> findByOwnerLike(final String owner,
final Pageable pageable) {
+ StringBuilder query = query(owner).append(" RETURN n.id");
+
+ if (pageable.isPaged()) {
+ query.append(" SKIP ").append(pageable.getPageSize() *
pageable.getPageNumber()).
+ append(" LIMIT ").append(pageable.getPageSize());
+ }
+
+ return toList(neo4jClient.query(query.toString()).fetch().all(),
"n.id", Neo4jAuthProfile.class, null);
+ }
+
+ @Override
+ public long countByOwnerLike(final String owner) {
+ StringBuilder query = query(owner).append(" RETURN COUNT(n.id)");
+
+ return neo4jTemplate.count(query.toString());
+ }
+
+}
diff --git
a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java
b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java
index 659a6cd81e..6286085f6b 100644
---
a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java
+++
b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java
@@ -40,6 +40,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.transaction.annotation.Transactional;
@@ -196,4 +197,15 @@ public class AuthProfileTest extends AbstractTest {
profile.setGoogleMfaAuthAccounts(List.of(account));
return authProfileDAO.save(profile);
}
+
+ @Test
+ public void findByOwnerLike() {
+ createAuthProfileWithAccount("owner1");
+ createAuthProfileWithAccount("owner2");
+ createAuthProfileWithAccount("test");
+
+ List<? extends AuthProfile> result =
authProfileDAO.findByOwnerLike("owner%", Pageable.unpaged());
+ assertEquals(2, result.size());
+ assertEquals(2, authProfileDAO.countByOwnerLike("owner%"));
+ }
}
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
index 759301ddb5..2a3d35ee97 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
@@ -20,7 +20,6 @@ package org.apache.syncope.fit.core.wa;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -31,6 +30,7 @@ import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.AuthProfileTO;
import org.apache.syncope.common.lib.to.PagedResult;
import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
+import org.apache.syncope.common.rest.api.beans.AuthProfileQuery;
import org.apache.syncope.fit.AbstractITCase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -65,17 +65,49 @@ public class GoogleMfaAuthTokenITCase extends
AbstractITCase {
}
@Test
- public void verifyProfile() {
- String owner = UUID.randomUUID().toString();
- GoogleMfaAuthToken token = createGoogleMfaAuthToken();
- GOOGLE_MFA_AUTH_TOKEN_SERVICE.store(owner, token);
- PagedResult<AuthProfileTO> results = AUTH_PROFILE_SERVICE.list(1, 100);
- assertFalse(results.getResult().isEmpty());
- AuthProfileTO profileTO = results.getResult().stream().
- filter(p -> owner.equals(p.getOwner())).findFirst().get();
- assertEquals(profileTO, AUTH_PROFILE_SERVICE.read(profileTO.getKey()));
- AUTH_PROFILE_SERVICE.delete(profileTO.getKey());
- assertThrows(SyncopeClientException.class, () ->
AUTH_PROFILE_SERVICE.read(profileTO.getKey()));
+ public void listProfiles() {
+ String owner = "owner" + UUID.randomUUID();
+ String owner1 = "owner" + UUID.randomUUID();
+ String test = "test" + UUID.randomUUID();
+ PagedResult<AuthProfileTO> results = null;
+ PagedResult<AuthProfileTO> resultsTest = null;
+ try {
+ GoogleMfaAuthToken token = createGoogleMfaAuthToken();
+ GOOGLE_MFA_AUTH_TOKEN_SERVICE.store(owner, token);
+
+ GoogleMfaAuthToken token1 = createGoogleMfaAuthToken();
+ GOOGLE_MFA_AUTH_TOKEN_SERVICE.store(owner1, token1);
+
+ GoogleMfaAuthToken token2 = createGoogleMfaAuthToken();
+ GOOGLE_MFA_AUTH_TOKEN_SERVICE.store(test, token2);
+
+ results = AUTH_PROFILE_SERVICE.search(
+ new
AuthProfileQuery.Builder().page(1).size(100).keyword("owner*").build());
+
+ resultsTest = AUTH_PROFILE_SERVICE.search(
+ new
AuthProfileQuery.Builder().page(1).size(100).keyword("test*").build());
+
+ assertEquals(2, results.getTotalCount());
+ assertEquals(2, results.getResult().size());
+ } finally {
+ AuthProfileTO profileTO = results.getResult().stream().
+ filter(p ->
owner.equals(p.getOwner())).findFirst().orElseThrow();
+ assertEquals(profileTO,
AUTH_PROFILE_SERVICE.read(profileTO.getKey()));
+ AUTH_PROFILE_SERVICE.delete(profileTO.getKey());
+ assertThrows(SyncopeClientException.class, () ->
AUTH_PROFILE_SERVICE.read(profileTO.getKey()));
+
+ AuthProfileTO profileTO1 = results.getResult().stream().
+ filter(p ->
owner1.equals(p.getOwner())).findFirst().orElseThrow();
+ assertEquals(profileTO1,
AUTH_PROFILE_SERVICE.read(profileTO1.getKey()));
+ AUTH_PROFILE_SERVICE.delete(profileTO1.getKey());
+ assertThrows(SyncopeClientException.class, () ->
AUTH_PROFILE_SERVICE.read(profileTO1.getKey()));
+
+ AuthProfileTO profileTO2 = resultsTest.getResult().stream().
+ filter(p ->
test.equals(p.getOwner())).findFirst().orElseThrow();
+ assertEquals(profileTO2,
AUTH_PROFILE_SERVICE.read(profileTO2.getKey()));
+ AUTH_PROFILE_SERVICE.delete(profileTO2.getKey());
+ assertThrows(SyncopeClientException.class, () ->
AUTH_PROFILE_SERVICE.read(profileTO2.getKey()));
+ }
}
@Test