From 6c553f2345597ddb5afc6d7615c93185efef35a2 Mon Sep 17 00:00:00 2001 From: aleksandrovpv Date: Wed, 17 Jun 2026 15:23:13 +0400 Subject: [PATCH 1/2] Search: document attribute-level access control and Search Strategy API #4200 release-3.0.adoc: - New Features: "Search Access Control by Attributes" - Breaking Changes: "Search" (attribute-level access, SearchStrategy API, SearchUtils removal) search-api.adoc: - "Access Control and Pagination" updated with the attribute-level step search-in-ui.adoc + search-ex1: - "Custom Search Strategies" updated to the new API: extend AbstractOpenSearchStrategy / AbstractElasticSearchStrategy and build the query via the platform QueryConfigurer so that the search scope and security policies are applied Co-Authored-By: Claude Opus 4.8 (1M context) --- .../CustomOpenSearchSearchStrategy.java | 29 ++++++++++++------- content/modules/search/pages/search-api.adoc | 2 +- .../modules/search/pages/search-in-ui.adoc | 22 ++++++++------ .../modules/whats-new/pages/release-3.0.adoc | 14 +++++++++ 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java b/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java index c96e81b3..39a2aab5 100644 --- a/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java +++ b/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java @@ -1,27 +1,36 @@ package com.company.demo.service; import io.jmix.search.searching.SearchRequestContext; -import io.jmix.searchopensearch.searching.strategy.OpenSearchSearchStrategy; +import io.jmix.searchopensearch.searching.strategy.OpenSearchQueryConfigurer; +import io.jmix.searchopensearch.searching.strategy.impl.AbstractOpenSearchStrategy; +import org.opensearch.client.opensearch._types.query_dsl.Operator; import org.opensearch.client.opensearch.core.SearchRequest; import org.springframework.stereotype.Component; // tag::strategy[] @Component -public class CustomOpenSearchSearchStrategy implements OpenSearchSearchStrategy { +public class CustomOpenSearchSearchStrategy extends AbstractOpenSearchStrategy { + + public CustomOpenSearchSearchStrategy(OpenSearchQueryConfigurer queryConfigurer) { + super(queryConfigurer); // <1> + } @Override public String getName() { - return "CustomStrategy"; + return "CustomStrategy"; // <2> } @Override - public void configureRequest(SearchRequestContext searchRequestContext) { - //configure your request - searchRequestContext.getRequestBuilder().query(queryBuilder -> - queryBuilder.multiMatch(multiMatchQueryBuilder -> - multiMatchQueryBuilder.fields("*") - .query(searchRequestContext.getSearchContext().getSearchText()) - ) + public void configureRequest(SearchRequestContext requestContext) { + queryConfigurer.configureRequest( // <3> + requestContext, + (queryBuilder, scope) -> // <4> + queryBuilder.multiMatch(multiMatchQueryBuilder -> + multiMatchQueryBuilder + .fields(scope.getFieldList()) // <5> + .query(requestContext.getSearchContext().getEscapedSearchText()) + .operator(Operator.Or) + ) ); } } diff --git a/content/modules/search/pages/search-api.adoc b/content/modules/search/pages/search-api.adoc index b872cfd1..e91bfcd7 100644 --- a/content/modules/search/pages/search-api.adoc +++ b/content/modules/search/pages/search-api.adoc @@ -97,7 +97,7 @@ The Search add-on provides an MBean with `jmix.search:type=EntityIndexing` objec Data access control is performed by the Search add-on in two steps: -* Pre-search - checks xref:security:resource-roles.adoc#entity-policy[entity policies] and excludes indexes related to forbidden entities. +* Pre-search - checks xref:security:resource-roles.adoc#entity-policy[entity policies] and excludes indexes related to forbidden entities. xref:security:resource-roles.adoc#entity-attribute-policy[Attribute policies] are applied as well: only entity attributes that the current user is allowed to read are included in the search query, so matches in restricted attributes are not returned. If the current user has no access to any of the requested entities, the search returns an empty result. * Post-search - checks if there are any xref:security:row-level-roles.adoc#policies[row-level policies] configured for found entities. If they exist, the found instances are reloaded to apply security policies. diff --git a/content/modules/search/pages/search-in-ui.adoc b/content/modules/search/pages/search-in-ui.adoc index b5bbaf30..8b74e87f 100644 --- a/content/modules/search/pages/search-in-ui.adoc +++ b/content/modules/search/pages/search-in-ui.adoc @@ -62,21 +62,25 @@ include::example$/search-ex1/src/main/resources/application.properties[tags=defa [[custom-search-strategies]] === Custom Search Strategies -Additionally, you can create a custom search strategy. For that purpose, you need to create a new Spring bean implementing one of the platform-specific interfaces: +Additionally, you can create a custom search strategy. Create a Spring bean that extends one of the platform-specific base classes: -* `OpenSearchSearchStrategy` - if you use OpenSearch. -* `ElasticsearchSearchStrategy` - if you use Elasticsearch. - -Then you need to implement 2 methods: - -* `String getName()` - should return a unique strategy name. - -* `void configureRequest(SearchRequest.Builder requestBuilder, SearchContext searchContext)` - configure your search request using the provided builder according to your requirements. +* `AbstractOpenSearchStrategy` - if you use OpenSearch. +* `AbstractElasticSearchStrategy` - if you use Elasticsearch. [source,java,indent=0] ---- include::example$/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java[tags=strategy] ---- +<1> The base class is initialized with the platform-specific query configurer (`OpenSearchQueryConfigurer` or `ElasticSearchQueryConfigurer`), available as the `queryConfigurer` field. +<2> `getName()` returns a unique strategy name used to select the strategy. +<3> Build the query through `queryConfigurer.configureRequest()`. The configurer resolves the search scope - the target indexes and the fields the current user is permitted to read - and applies security policies. +<4> The lambda receives the platform-specific query builder and the resolved `IndexSearchRequestScope`. +<5> Use `scope.getFieldList()` to search only the permitted fields. + +[NOTE] +==== +Always build the query through `queryConfigurer`. If you configure the request builder directly (for example, with `fields("*")`), the search scope is not resolved and xref:security:resource-roles.adoc#entity-attribute-policy[attribute policies] are not applied, which can make the search return no results. +==== After that, you can assign your custom strategy to the xref:search-field-component.adoc[SearchField] or xref:full-text-filter-component.adoc[FullTextFilter] component using the strategy name. diff --git a/content/modules/whats-new/pages/release-3.0.adoc b/content/modules/whats-new/pages/release-3.0.adoc index 8d0e7e5a..7fff0c67 100644 --- a/content/modules/whats-new/pages/release-3.0.adoc +++ b/content/modules/whats-new/pages/release-3.0.adoc @@ -123,6 +123,11 @@ In addition to application-wide sorting customization, developers can now custom This approach keeps sorting rules close to the grid configuration. It is useful when sorting requirements are specific to a single view. +[[search-attribute-access]] +=== Search Access Control by Attributes + +xref:search:index.adoc[Search] now takes entity attribute permissions into account: the search query includes only the attributes that the current user is allowed to read according to their xref:security:resource-roles.adoc[resource roles], so matches in restricted attributes are no longer returned. See xref:search:search-api.adoc#security-and-pagination[Access Control and Pagination] for details. + [[breaking-changes]] == Breaking Changes @@ -279,6 +284,15 @@ Getter return types changed in the following properties: See https://github.com/jmix-framework/jmix/issues/5262[#5262^] for more information. +[[search-breaking-changes]] +=== Search + +* Full-text search now applies attribute read permissions from xref:security:resource-roles.adoc[resource roles] when building the query: only attributes that the current user is allowed to read are searched, so matches in restricted attributes are no longer returned. xref:security:row-level-roles.adoc[Row-level roles] are still applied to the found records. No migration is required; the behavior is applied automatically. + +* The search strategy API has changed. `SearchStrategy` is now generic (`SearchStrategy`) with a single `configureRequest(SearchRequestContext)` method, and `ElasticsearchSearchStrategy` / `OpenSearchSearchStrategy` extend `SearchStrategy`. The previous `configureRequest(SearchRequest.Builder, SearchContext)` method and the `SearchUtils` class have been removed. If you implement a custom search strategy, build the request in `configureRequest(SearchRequestContext)` using the platform-specific `ElasticSearchQueryConfigurer` / `OpenSearchQueryConfigurer`, which resolves the permitted search scope (indexes and fields) and applies security policies. + +See https://github.com/jmix-framework/jmix/issues/4200[#4200^] for more information. + [[removed-dependencies]] === Removed Dependencies From 50d5437e0d428d3bc6363359706d5b92e4e0721f Mon Sep 17 00:00:00 2001 From: aleksandrovpv Date: Thu, 25 Jun 2026 15:01:06 +0400 Subject: [PATCH 2/2] Search docs: address review, correct release-notes versions #4200 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - search-in-ui.adoc + example: describe the configureRequest(SearchRequestContext) method explicitly (review comment), with a dedicated callout - release-3.0.adoc: attribute-level access is a 2.7 feature, not 3.0 — keep only the 3.0 removal of the API deprecated in 2.7 (#4866) - search-api.adoc: mark the attribute-level behavior as "Since Jmix 2.7" Co-Authored-By: Claude Opus 4.8 (1M context) --- .../service/CustomOpenSearchSearchStrategy.java | 8 ++++---- content/modules/search/pages/search-api.adoc | 2 +- content/modules/search/pages/search-in-ui.adoc | 9 ++++++--- content/modules/whats-new/pages/release-3.0.adoc | 15 +++++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java b/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java index 39a2aab5..b053a54c 100644 --- a/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java +++ b/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java @@ -21,13 +21,13 @@ public String getName() { } @Override - public void configureRequest(SearchRequestContext requestContext) { - queryConfigurer.configureRequest( // <3> + public void configureRequest(SearchRequestContext requestContext) { // <3> + queryConfigurer.configureRequest( // <4> requestContext, - (queryBuilder, scope) -> // <4> + (queryBuilder, scope) -> // <5> queryBuilder.multiMatch(multiMatchQueryBuilder -> multiMatchQueryBuilder - .fields(scope.getFieldList()) // <5> + .fields(scope.getFieldList()) // <6> .query(requestContext.getSearchContext().getEscapedSearchText()) .operator(Operator.Or) ) diff --git a/content/modules/search/pages/search-api.adoc b/content/modules/search/pages/search-api.adoc index e91bfcd7..103271f5 100644 --- a/content/modules/search/pages/search-api.adoc +++ b/content/modules/search/pages/search-api.adoc @@ -97,7 +97,7 @@ The Search add-on provides an MBean with `jmix.search:type=EntityIndexing` objec Data access control is performed by the Search add-on in two steps: -* Pre-search - checks xref:security:resource-roles.adoc#entity-policy[entity policies] and excludes indexes related to forbidden entities. xref:security:resource-roles.adoc#entity-attribute-policy[Attribute policies] are applied as well: only entity attributes that the current user is allowed to read are included in the search query, so matches in restricted attributes are not returned. If the current user has no access to any of the requested entities, the search returns an empty result. +* Pre-search - checks xref:security:resource-roles.adoc#entity-policy[entity policies] and excludes indexes related to forbidden entities. Since Jmix 2.7, xref:security:resource-roles.adoc#entity-attribute-policy[attribute policies] are applied as well: only entity attributes that the current user is allowed to read are included in the search query, so matches in restricted attributes are not returned. If the current user has no access to any of the requested entities, the search returns an empty result. * Post-search - checks if there are any xref:security:row-level-roles.adoc#policies[row-level policies] configured for found entities. If they exist, the found instances are reloaded to apply security policies. diff --git a/content/modules/search/pages/search-in-ui.adoc b/content/modules/search/pages/search-in-ui.adoc index 8b74e87f..656bcd29 100644 --- a/content/modules/search/pages/search-in-ui.adoc +++ b/content/modules/search/pages/search-in-ui.adoc @@ -67,15 +67,18 @@ Additionally, you can create a custom search strategy. Create a Spring bean that * `AbstractOpenSearchStrategy` - if you use OpenSearch. * `AbstractElasticSearchStrategy` - if you use Elasticsearch. +Implement `getName()` and `configureRequest(SearchRequestContext)`: + [source,java,indent=0] ---- include::example$/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java[tags=strategy] ---- <1> The base class is initialized with the platform-specific query configurer (`OpenSearchQueryConfigurer` or `ElasticSearchQueryConfigurer`), available as the `queryConfigurer` field. <2> `getName()` returns a unique strategy name used to select the strategy. -<3> Build the query through `queryConfigurer.configureRequest()`. The configurer resolves the search scope - the target indexes and the fields the current user is permitted to read - and applies security policies. -<4> The lambda receives the platform-specific query builder and the resolved `IndexSearchRequestScope`. -<5> Use `scope.getFieldList()` to search only the permitted fields. +<3> `configureRequest(SearchRequestContext)` is the method the search engine invokes for the strategy — implement it to build the search request. +<4> Build the query through `queryConfigurer.configureRequest()`. The configurer resolves the search scope - the target indexes and the fields the current user is permitted to read - and applies security policies. +<5> The lambda receives the platform-specific query builder and the resolved `IndexSearchRequestScope`. +<6> Use `scope.getFieldList()` to search only the permitted fields. [NOTE] ==== diff --git a/content/modules/whats-new/pages/release-3.0.adoc b/content/modules/whats-new/pages/release-3.0.adoc index 7fff0c67..6fd0d3d9 100644 --- a/content/modules/whats-new/pages/release-3.0.adoc +++ b/content/modules/whats-new/pages/release-3.0.adoc @@ -123,11 +123,6 @@ In addition to application-wide sorting customization, developers can now custom This approach keeps sorting rules close to the grid configuration. It is useful when sorting requirements are specific to a single view. -[[search-attribute-access]] -=== Search Access Control by Attributes - -xref:search:index.adoc[Search] now takes entity attribute permissions into account: the search query includes only the attributes that the current user is allowed to read according to their xref:security:resource-roles.adoc[resource roles], so matches in restricted attributes are no longer returned. See xref:search:search-api.adoc#security-and-pagination[Access Control and Pagination] for details. - [[breaking-changes]] == Breaking Changes @@ -287,11 +282,15 @@ See https://github.com/jmix-framework/jmix/issues/5262[#5262^] for more informat [[search-breaking-changes]] === Search -* Full-text search now applies attribute read permissions from xref:security:resource-roles.adoc[resource roles] when building the query: only attributes that the current user is allowed to read are searched, so matches in restricted attributes are no longer returned. xref:security:row-level-roles.adoc[Row-level roles] are still applied to the found records. No migration is required; the behavior is applied automatically. +The search strategy API and helper classes deprecated in 2.7 have been removed: + +* The `configureRequest(SearchRequest.Builder, SearchContext)` method of `ElasticsearchSearchStrategy` / `OpenSearchSearchStrategy`. Implement `configureRequest(SearchRequestContext)` instead and build the query through the platform-specific `ElasticSearchQueryConfigurer` / `OpenSearchQueryConfigurer`. + +* The `SearchUtils` class. Use `SearchRequestScopeProvider`, `SearchFieldsProvider`, and `SearchSecurityDecorator` instead. -* The search strategy API has changed. `SearchStrategy` is now generic (`SearchStrategy`) with a single `configureRequest(SearchRequestContext)` method, and `ElasticsearchSearchStrategy` / `OpenSearchSearchStrategy` extend `SearchStrategy`. The previous `configureRequest(SearchRequest.Builder, SearchContext)` method and the `SearchUtils` class have been removed. If you implement a custom search strategy, build the request in `configureRequest(SearchRequestContext)` using the platform-specific `ElasticSearchQueryConfigurer` / `OpenSearchQueryConfigurer`, which resolves the permitted search scope (indexes and fields) and applies security policies. +If you implemented a custom search strategy, migrate it to the new API — see xref:search:search-in-ui.adoc#custom-search-strategies[Custom Search Strategies]. -See https://github.com/jmix-framework/jmix/issues/4200[#4200^] for more information. +See https://github.com/jmix-framework/jmix/issues/4866[#4866^] for more information. [[removed-dependencies]] === Removed Dependencies