From 35229fbe5c570918f6a6f92f25621b88a611277e Mon Sep 17 00:00:00 2001 From: Xun Zhang Date: Tue, 23 Jan 2024 07:09:39 -0800 Subject: [PATCH 1/7] Adds new ml-commons system indices to the list (#3973) ### Description Adds renamed ml-commons systems indices to the list of existing system indices. This change is required so that security plugin correctly recognizes the new indices as system indices. * Category - Maintenance Signed-off-by: Xun Zhang --- .../security/tools/democonfig/SecuritySettingsConfigurer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java b/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java index 9c51fbe1d4..a68c93f03f 100644 --- a/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java +++ b/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java @@ -55,6 +55,8 @@ public class SecuritySettingsConfigurer { ".plugins-ml-task", ".plugins-ml-conversation-meta", ".plugins-ml-conversation-interactions", + ".plugins-ml-memory-meta", + ".plugins-ml-memory-message", ".opendistro-alerting-config", ".opendistro-alerting-alert*", ".opendistro-anomaly-results*", From dadd03fcd7a856df895bb2299761fc7783dfe62b Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Tue, 23 Jan 2024 12:00:30 -0500 Subject: [PATCH 2/7] Fix: remove unnecessary trailing slashes in APIs. (#3976) ### Description Coming from https://github.com/opensearch-project/opensearch-api-specification/pull/179 which flags a couple of false positives because of mismatched trailing slash. ### Check List - [x] New functionality includes testing - [x] New functionality has been documented - [x] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). Signed-off-by: dblock --- .../security/dlic/rest/api/ActionGroupsApiAction.java | 6 +++--- .../opensearch/security/dlic/rest/api/AuditApiAction.java | 4 ++-- .../security/dlic/rest/api/InternalUsersApiAction.java | 6 +++--- .../opensearch/security/dlic/rest/api/NodesDnApiAction.java | 4 ++-- .../opensearch/security/dlic/rest/api/RolesApiAction.java | 4 ++-- .../security/dlic/rest/api/RolesMappingApiAction.java | 4 ++-- .../security/dlic/rest/api/SecuritySSLCertsApiAction.java | 2 +- .../opensearch/security/dlic/rest/api/TenantsApiAction.java | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java index 172d4a537b..3032054e64 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java @@ -46,16 +46,16 @@ public class ActionGroupsApiAction extends AbstractApiAction { // legacy mapping for backwards compatibility // TODO: remove in next version new Route(Method.GET, "/actiongroup/{name}"), - new Route(Method.GET, "/actiongroup/"), + new Route(Method.GET, "/actiongroup"), new Route(Method.DELETE, "/actiongroup/{name}"), new Route(Method.PUT, "/actiongroup/{name}"), // corrected mapping, introduced in OpenSearch Security new Route(Method.GET, "/actiongroups/{name}"), - new Route(Method.GET, "/actiongroups/"), + new Route(Method.GET, "/actiongroups"), new Route(Method.DELETE, "/actiongroups/{name}"), new Route(Method.PUT, "/actiongroups/{name}"), - new Route(Method.PATCH, "/actiongroups/"), + new Route(Method.PATCH, "/actiongroups"), new Route(Method.PATCH, "/actiongroups/{name}") ) diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java index 47bc1f184e..997bd85bdd 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java @@ -123,9 +123,9 @@ public class AuditApiAction extends AbstractApiAction { private static final List routes = addRoutesPrefix( ImmutableList.of( - new Route(RestRequest.Method.GET, "/audit/"), + new Route(RestRequest.Method.GET, "/audit"), new Route(RestRequest.Method.PUT, "/audit/config"), - new Route(RestRequest.Method.PATCH, "/audit/") + new Route(RestRequest.Method.PATCH, "/audit") ) ); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java index 70994504bf..3cbcc18bd9 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java @@ -64,18 +64,18 @@ protected void consumeParameters(final RestRequest request) { private static final List routes = addRoutesPrefix( ImmutableList.of( new Route(Method.GET, "/user/{name}"), - new Route(Method.GET, "/user/"), + new Route(Method.GET, "/user"), new Route(Method.POST, "/user/{name}/authtoken"), new Route(Method.DELETE, "/user/{name}"), new Route(Method.PUT, "/user/{name}"), // corrected mapping, introduced in OpenSearch Security new Route(Method.GET, "/internalusers/{name}"), - new Route(Method.GET, "/internalusers/"), + new Route(Method.GET, "/internalusers"), new Route(Method.POST, "/internalusers/{name}/authtoken"), new Route(Method.DELETE, "/internalusers/{name}"), new Route(Method.PUT, "/internalusers/{name}"), - new Route(Method.PATCH, "/internalusers/"), + new Route(Method.PATCH, "/internalusers"), new Route(Method.PATCH, "/internalusers/{name}") ) ); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java index 05c533b1d9..ff44867bd2 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java @@ -62,10 +62,10 @@ public class NodesDnApiAction extends AbstractApiAction { private static final List routes = addRoutesPrefix( ImmutableList.of( new Route(Method.GET, "/nodesdn/{name}"), - new Route(Method.GET, "/nodesdn/"), + new Route(Method.GET, "/nodesdn"), new Route(Method.DELETE, "/nodesdn/{name}"), new Route(Method.PUT, "/nodesdn/{name}"), - new Route(Method.PATCH, "/nodesdn/"), + new Route(Method.PATCH, "/nodesdn"), new Route(Method.PATCH, "/nodesdn/{name}") ) ); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java index 9af04d17ec..50fac9b80c 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java @@ -45,11 +45,11 @@ public class RolesApiAction extends AbstractApiAction { private static final List routes = addRoutesPrefix( ImmutableList.of( - new Route(Method.GET, "/roles/"), + new Route(Method.GET, "/roles"), new Route(Method.GET, "/roles/{name}"), new Route(Method.DELETE, "/roles/{name}"), new Route(Method.PUT, "/roles/{name}"), - new Route(Method.PATCH, "/roles/"), + new Route(Method.PATCH, "/roles"), new Route(Method.PATCH, "/roles/{name}") ) ); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java index 230ce0e1a1..b980a1e4ba 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java @@ -38,11 +38,11 @@ public class RolesMappingApiAction extends AbstractApiAction { private static final List routes = addRoutesPrefix( ImmutableList.of( - new Route(Method.GET, "/rolesmapping/"), + new Route(Method.GET, "/rolesmapping"), new Route(Method.GET, "/rolesmapping/{name}"), new Route(Method.DELETE, "/rolesmapping/{name}"), new Route(Method.PUT, "/rolesmapping/{name}"), - new Route(Method.PATCH, "/rolesmapping/"), + new Route(Method.PATCH, "/rolesmapping"), new Route(Method.PATCH, "/rolesmapping/{name}") ) ); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java index 48e1c9b704..e60070288e 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java @@ -50,7 +50,7 @@ */ public class SecuritySSLCertsApiAction extends AbstractApiAction { private static final List ROUTES = addRoutesPrefix( - ImmutableList.of(new Route(Method.GET, "/ssl/certs"), new Route(Method.PUT, "/ssl/{certType}/reloadcerts/")) + ImmutableList.of(new Route(Method.GET, "/ssl/certs"), new Route(Method.PUT, "/ssl/{certType}/reloadcerts")) ); private final SecurityKeyStore securityKeyStore; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java index 28fd6dcdcb..e16d31ba6f 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java @@ -50,10 +50,10 @@ public class TenantsApiAction extends AbstractApiAction { private static final List routes = addRoutesPrefix( ImmutableList.of( new Route(Method.GET, "/tenants/{name}"), - new Route(Method.GET, "/tenants/"), + new Route(Method.GET, "/tenants"), new Route(Method.DELETE, "/tenants/{name}"), new Route(Method.PUT, "/tenants/{name}"), - new Route(Method.PATCH, "/tenants/"), + new Route(Method.PATCH, "/tenants"), new Route(Method.PATCH, "/tenants/{name}") ) ); From d44113808659ddc8e57f8fd3f67ba343cc4f462f Mon Sep 17 00:00:00 2001 From: Stephen Crawford <65832608+scrawfor99@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:43:35 -0500 Subject: [PATCH 3/7] Bump spotless (6.24.0 -> 6.25.0) to bump eclipse resources (3.18 -> 3.19) (#3992) ### Description This PR bumps spotless to bump the transient dependency on org.eclipse.platform:org.eclipse.core.resources@3.18.100 -> org.eclipse.platform:org.eclipse.core.resources@3.19.100. In turn this should stop scanners from reporting the project as vulnerable to: https://nvd.nist.gov/vuln/detail/CVE-2023-4218. I was not able to easily move just the Eclipse dependency because it seems that the package causing the flagging org.eclipse.platform:org.eclipse.core.resources@3.18.100 does not have a straight path forward to the recommended versions listed on the CVE. However, https://security.snyk.io/package/maven/org.eclipse.platform:org.eclipse.core.resources/3.19.100 reports that this version should remove the issue while https://security.snyk.io/package/maven/org.eclipse.platform:org.eclipse.core.resources/3.18.100 will cause the flag. One note: We should not actually be concerned about this issue as it is related to Eclipse IDE behavior and nothing to do with the type of dependency on the Eclipse packages like we have. ### Check List - [ ] ~New functionality includes testing~ - [ ] ~New functionality has been documented~ - [x] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). Signed-off-by: Stephen Crawford --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 67b9b60a9c..db7af24910 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,7 @@ plugins { id 'idea' id 'jacoco' id 'maven-publish' - id 'com.diffplug.spotless' version '6.24.0' + id 'com.diffplug.spotless' version '6.25.0' id 'checkstyle' id 'com.netflix.nebula.ospackage' version "11.6.0" id "org.gradle.test-retry" version "1.5.8" From af7c7e4c6fe64b9719dad09acf2eec907afb31cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:32:56 +0100 Subject: [PATCH 4/7] Bump io.dropwizard.metrics:metrics-core from 4.2.24 to 4.2.25 (#3996) Bumps [io.dropwizard.metrics:metrics-core](https://github.com/dropwizard/metrics) from 4.2.24 to 4.2.25.
Commits
  • 7c2ffc5 [maven-release-plugin] prepare release v4.2.25
  • b116e89 Jakarta HealthCheckServlet object mapper and status indicator (#3924)
  • 5255717 Update dependency org.assertj:assertj-core to v3.25.2
  • 0aada3f Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.3.1
  • 29ce821 [maven-release-plugin] prepare for next development iteration
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=io.dropwizard.metrics:metrics-core&package-manager=gradle&previous-version=4.2.24&new-version=4.2.25)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index db7af24910..6659bc8470 100644 --- a/build.gradle +++ b/build.gradle @@ -643,7 +643,7 @@ dependencies { runtimeOnly 'com.google.j2objc:j2objc-annotations:2.8' compileOnly 'com.google.code.findbugs:jsr305:3.0.2' runtimeOnly 'org.lz4:lz4-java:1.8.0' - runtimeOnly 'io.dropwizard.metrics:metrics-core:4.2.24' + runtimeOnly 'io.dropwizard.metrics:metrics-core:4.2.25' runtimeOnly 'org.slf4j:slf4j-api:1.7.36' runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}" runtimeOnly 'org.xerial.snappy:snappy-java:1.1.10.5' From 4525c958972cbee29e047a8623263c1413c31654 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:33:34 +0100 Subject: [PATCH 5/7] Bump jjwt_version from 0.12.3 to 0.12.4 (#3995) Bumps `jjwt_version` from 0.12.3 to 0.12.4. Updates `io.jsonwebtoken:jjwt-api` from 0.12.3 to 0.12.4
Release notes

Sourced from io.jsonwebtoken:jjwt-api's releases.

0.12.4

This is patch release completes 10 issues, with two especially noteworthy changes, and a number of other smaller bug fixes and enhancements.

  1. The default Jackson deserializer will now reject duplicate JSON members by default in an attempt to be a little more strict at rejecting potentially malicious or malformed JSON. This is a default and can be overridden with a custom ObjectMapper if desired.
  2. Password-based JWE encryption key algorithms (PBES2_HS256_A128KW, PBES2_HS384_A192KW and PBES2_HS512_A256KW) now enforce an upper bound (maximum) number of iterations allowed during decryption to mitigate against potential DoS attacks. Many thanks to Jingcheng Yang and Jianjun Chen from Sichuan University and Zhongguancun Lab for their work on this!

A number of other issues fixed: thread-safe ServiceLoader usage for dynamic JSON processor lookup, Android enhancements for JSON Reader APIs, fixed Elliptic Curve field element padding, and more. Please read the 0.12.4 CHANGELOG for full details of all of these changes, and as always, project documentation is in the 0.12.4 README.

Please allow 30 minutes from the time this announcement is published for the release to be available in Maven Central.

Changelog

Sourced from io.jsonwebtoken:jjwt-api's changelog.

0.12.4

This patch release includes various changes listed below.

Jackson Default Parsing Behavior

This release makes two behavioral changes to JJWT's default Jackson ObjectMapper parsing settings:

  1. In the interest of having stronger standards to reject potentially malformed/malicious/accidental JSON that could have undesirable effects on an application, JJWT's default ObjectMapper is now configured to explicitly reject/fail parsing JSON (JWT headers and/or Claims) if/when that JSON contains duplicate JSON member names.

    For example, now the following JSON, if parsed, would fail (be rejected) by default:

    {
      "hello": "world",
      "thisWillFail": 42,
      "thisWillFail": "test"
    }
    

    Technically, the JWT RFCs do allow duplicate named fields as long as the last parsed member is the one used (see JWS RFC 7515, Section 4), so this is allowed. However, because JWTs often reflect security concepts, it's usually better to be defensive and reject these unexpected scenarios by default. The RFC later supports this position/preference in Section 10.12:

    Ambiguous and potentially exploitable situations
    could arise if the JSON parser used does not enforce the uniqueness
    of member names or returns an unpredictable value for duplicate
    member names.
    

    Finally, this is just a default, and the RFC does indeed allow duplicate member names if the last value is used, so applications that require duplicates to be allowed can simply configure their own ObjectMapper and use that with JJWT instead of assuming this (new) JJWT default. See [Issue #877](jwtk/jjwt#877) for more.

  2. If using JJWT's support to use Jackson to parse Custom Claim Types (for example, a Claim that should be unmarshalled into a POJO), and the JSON for that POJO contained a member that is not represented in the specified class, Jackson would fail parsing by default. Because POJOs and JSON data models can sometimes be out of sync due to different class versions, the default behavior has been changed to ignore these unknown JSON members instead of failing (i.e. the ObjectMapper's DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is now set to false) by default.

    Again, if you prefer the stricter behavior of rejecting JSON with extra or unknown properties, you can configure true on your own ObjectMapper instance and use that instance with the Jwts.parser() builder.

Additional Changes

This release also:

... (truncated)

Commits
  • bf4168c [maven-release-plugin] prepare release 0.12.4
  • 5c6dec0 - Adding 0.12.4 release version references
  • dd10b12 Added JWK Set documentation to README.mdJwkset doc (#912)
  • 6335381 PBES2 decryption maximum iterations (#911)
  • 2884eb7 - Updating to GitHub latest actions/checkout and actions/setup-java script ve...
  • 628bd6f Secret JWK k values larger than HMAC-SHA minimums (#909)
  • b12dabf Fix small typos (#908)
  • 26f5dc3 Updating changelog with more information/clarity for the 0.12.4 release (#907)
  • f61cfa8 Test case change to reflect accurate assertion for Elliptic Curve 'd' values ...
  • fd619e0 disable FAIL_ON_UNKNOWN_PROPERTIES deserialization feature of Jackson by defa...
  • Additional commits viewable in compare view

Updates `io.jsonwebtoken:jjwt-impl` from 0.12.3 to 0.12.4
Release notes

Sourced from io.jsonwebtoken:jjwt-impl's releases.

0.12.4

This is patch release completes 10 issues, with two especially noteworthy changes, and a number of other smaller bug fixes and enhancements.

  1. The default Jackson deserializer will now reject duplicate JSON members by default in an attempt to be a little more strict at rejecting potentially malicious or malformed JSON. This is a default and can be overridden with a custom ObjectMapper if desired.
  2. Password-based JWE encryption key algorithms (PBES2_HS256_A128KW, PBES2_HS384_A192KW and PBES2_HS512_A256KW) now enforce an upper bound (maximum) number of iterations allowed during decryption to mitigate against potential DoS attacks. Many thanks to Jingcheng Yang and Jianjun Chen from Sichuan University and Zhongguancun Lab for their work on this!

A number of other issues fixed: thread-safe ServiceLoader usage for dynamic JSON processor lookup, Android enhancements for JSON Reader APIs, fixed Elliptic Curve field element padding, and more. Please read the 0.12.4 CHANGELOG for full details of all of these changes, and as always, project documentation is in the 0.12.4 README.

Please allow 30 minutes from the time this announcement is published for the release to be available in Maven Central.

Changelog

Sourced from io.jsonwebtoken:jjwt-impl's changelog.

0.12.4

This patch release includes various changes listed below.

Jackson Default Parsing Behavior

This release makes two behavioral changes to JJWT's default Jackson ObjectMapper parsing settings:

  1. In the interest of having stronger standards to reject potentially malformed/malicious/accidental JSON that could have undesirable effects on an application, JJWT's default ObjectMapper is now configured to explicitly reject/fail parsing JSON (JWT headers and/or Claims) if/when that JSON contains duplicate JSON member names.

    For example, now the following JSON, if parsed, would fail (be rejected) by default:

    {
      "hello": "world",
      "thisWillFail": 42,
      "thisWillFail": "test"
    }
    

    Technically, the JWT RFCs do allow duplicate named fields as long as the last parsed member is the one used (see JWS RFC 7515, Section 4), so this is allowed. However, because JWTs often reflect security concepts, it's usually better to be defensive and reject these unexpected scenarios by default. The RFC later supports this position/preference in Section 10.12:

    Ambiguous and potentially exploitable situations
    could arise if the JSON parser used does not enforce the uniqueness
    of member names or returns an unpredictable value for duplicate
    member names.
    

    Finally, this is just a default, and the RFC does indeed allow duplicate member names if the last value is used, so applications that require duplicates to be allowed can simply configure their own ObjectMapper and use that with JJWT instead of assuming this (new) JJWT default. See [Issue #877](jwtk/jjwt#877) for more.

  2. If using JJWT's support to use Jackson to parse Custom Claim Types (for example, a Claim that should be unmarshalled into a POJO), and the JSON for that POJO contained a member that is not represented in the specified class, Jackson would fail parsing by default. Because POJOs and JSON data models can sometimes be out of sync due to different class versions, the default behavior has been changed to ignore these unknown JSON members instead of failing (i.e. the ObjectMapper's DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is now set to false) by default.

    Again, if you prefer the stricter behavior of rejecting JSON with extra or unknown properties, you can configure true on your own ObjectMapper instance and use that instance with the Jwts.parser() builder.

Additional Changes

This release also:

... (truncated)

Commits
  • bf4168c [maven-release-plugin] prepare release 0.12.4
  • 5c6dec0 - Adding 0.12.4 release version references
  • dd10b12 Added JWK Set documentation to README.mdJwkset doc (#912)
  • 6335381 PBES2 decryption maximum iterations (#911)
  • 2884eb7 - Updating to GitHub latest actions/checkout and actions/setup-java script ve...
  • 628bd6f Secret JWK k values larger than HMAC-SHA minimums (#909)
  • b12dabf Fix small typos (#908)
  • 26f5dc3 Updating changelog with more information/clarity for the 0.12.4 release (#907)
  • f61cfa8 Test case change to reflect accurate assertion for Elliptic Curve 'd' values ...
  • fd619e0 disable FAIL_ON_UNKNOWN_PROPERTIES deserialization feature of Jackson by defa...
  • Additional commits viewable in compare view

Updates `io.jsonwebtoken:jjwt-jackson` from 0.12.3 to 0.12.4 Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6659bc8470..e0ae931d5a 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ buildscript { apache_cxf_version = '4.0.3' open_saml_version = '4.3.0' one_login_java_saml = '2.9.0' - jjwt_version = '0.12.3' + jjwt_version = '0.12.4' guava_version = '32.1.3-jre' jaxb_version = '2.3.9' spring_version = '5.3.31' From 157d137bd85696d55c6c78ba72124656d4cb5d5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:04:22 -0500 Subject: [PATCH 6/7] Bump gradle/gradle-build-action from 2 to 3 (#3994) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [gradle/gradle-build-action](https://github.com/gradle/gradle-build-action) from 2 to 3.
Release notes

Sourced from gradle/gradle-build-action's releases.

v3.0.0-rc.1

First release candidate of gradle/gradle-build-action@v3.0.0. This release candidate will the first release available under the v3 version tag.

[!IMPORTANT] As of v3 this action has been superceded by gradle/actions/setup-gradle. Any workflow that uses gradle/gradle-build-action@v3 will transparently delegate to gradle/actions/setup-gradle@v3.

Users are encouraged to update their workflows, replacing:

uses: gradle/gradle-build-action@v3

with

uses: gradle/actions/setup-gradle@v3

See the setup-gradle documentation for up-to-date documentation for gradle/actons/setup-gradle.

Changes from gradle-build-action@v2

This release brings some useful and much requested features, including:

  • save and restore the Gradle configuration-cache data
  • add the Job summary content as a PR comment
  • easily publish Build ScansĀ® to the free Gradle Build Scan service
  • compatibility with Node 20

The only major breaking change from gradle-build-action@v2.12.0 is the update to require a Node 20 runtime environment. Aside from that change, this release should generally serve as a drop-in replacement for gradle-build-action@v2.

Changelog

  • [NEW] - Run with NodeJs 20.x (gradle/gradle-build-action#946)
  • [NEW] - Support for save & restore of configuration-cache data (gradle/gradle-build-action#966)
  • [NEW] - Support for automatic adding PR comment with Job Summary content (gradle/gradle-build-action#1020)
  • [NEW] - Make it easy to publish a Build ScanĀ® to https://scans.gradle.com (gradle/gradle-build-action#1044)
  • [NEW] - Added dependency-graph-continue-on-failure input, which can be set to false to force the Job to fail when dependency graph submission fails (gradle/gradle-build-action#1036). Failure modes include:
  • [NEW] - Add dependency-graph: clear option to clear any dependency-graph previously submitted by the job
  • [FIX] Allow cache entries to be reused by jobs with the same ID in different workflows (gradle/gradle-build-action#1017)
    • Workflow name remains part of the cache key, but cache entries generated by the same job id in a different workflow may be restored
  • [FIX] Register pre-installed JDKs in Maven toolchains.xml file (gradle/gradle-build-action#1024)
    • This allows pre-installed JDKs to be auto-detected by Gradle Toolchain support on Windows
  • [FIX] - Update the Gradle Enterprise injection configuration for product rename to Develocity (gradle/gradle-build-action#995)
  • [FIX] - Avoid submitting an empty dependency graph when state is loaded from configuration-cache
  • [DEPRECATION] - Deprecation of the arguments parameter (gradle/gradle-build-action#996)
  • [BREAKING CHANGE] - Remove the gradle-executable input parameter. Use a separate workflow Step to execute a Gradle from a custom location.

... (truncated)

Commits
  • 4a8703f Delegate to 'setup-gradle@v3.0.0-rc.1'
  • 4a39eed Mention setup-gradle in README
  • 272883a Remove all action sources: these have been migrated to 'gradle/actions'
  • 2a8bfcf Delegate action implementation to gradle/actions/setup-gradle
  • e1ada08 Bump the github-actions group with 1 update (#1047)
  • a8e3e5e Apply dependency version updates
  • 2be01ca Build outputs
  • a00827e Bump the npm-dependencies group with 7 updates
  • ad80850 Bump the github-actions group with 2 updates
  • bd6d0a7 Configure explicit java version for config-cache test
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=gradle/gradle-build-action&package-manager=github_actions&previous-version=2&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/code-hygiene.yml | 6 +++--- .github/workflows/plugin_install.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f64a0408b..acd9b9de27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v4 - name: Build and Test - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: | @@ -112,7 +112,7 @@ jobs: uses: actions/checkout@v4 - name: Build and Test - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: | @@ -147,7 +147,7 @@ jobs: uses: actions/checkout@v4 - name: Build and Test - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: | @@ -165,7 +165,7 @@ jobs: uses: actions/checkout@v4 - name: Build BWC tests - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: | diff --git a/.github/workflows/code-hygiene.yml b/.github/workflows/code-hygiene.yml index e6afd8fede..2f8820709a 100644 --- a/.github/workflows/code-hygiene.yml +++ b/.github/workflows/code-hygiene.yml @@ -24,7 +24,7 @@ jobs: distribution: temurin # Temurin is a distribution of adoptium java-version: 17 - - uses: gradle/gradle-build-action@v2 + - uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: spotlessCheck @@ -40,7 +40,7 @@ jobs: distribution: temurin # Temurin is a distribution of adoptium java-version: 11 - - uses: gradle/gradle-build-action@v2 + - uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: checkstyleMain checkstyleTest checkstyleIntegrationTest @@ -56,7 +56,7 @@ jobs: distribution: temurin # Temurin is a distribution of adoptium java-version: 11 - - uses: gradle/gradle-build-action@v2 + - uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: spotbugsMain diff --git a/.github/workflows/plugin_install.yml b/.github/workflows/plugin_install.yml index c051e2f6a3..b88cfb166f 100644 --- a/.github/workflows/plugin_install.yml +++ b/.github/workflows/plugin_install.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v4 - name: Assemble target plugin - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: assemble @@ -63,7 +63,7 @@ jobs: admin-password: ${{ steps.random-password.outputs.generated_name }} - name: Run sanity tests - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v3 with: cache-disabled: true arguments: integTestRemote -Dtests.rest.cluster=localhost:9200 -Dtests.cluster=localhost:9200 -Dtests.clustername="opensearch" -Dhttps=true -Duser=admin -Dpassword=${{ steps.random-password.outputs.generated_name }} -i From c06365ca94c9a1d15e85b578a1ae48168bf0bca7 Mon Sep 17 00:00:00 2001 From: Prabhas Kurapati <66924475+prabhask5@users.noreply.github.com> Date: Mon, 29 Jan 2024 06:54:56 -0800 Subject: [PATCH 7/7] [BUG-2556] Add new DLS filtering test (#3908) Signed-off-by: Prabhas Kurapati --- .../security/DlsIntegrationTests.java | 205 +++++++++++++++++- 1 file changed, 204 insertions(+), 1 deletion(-) diff --git a/src/integrationTest/java/org/opensearch/security/DlsIntegrationTests.java b/src/integrationTest/java/org/opensearch/security/DlsIntegrationTests.java index aa7202cddf..3e3ac61502 100644 --- a/src/integrationTest/java/org/opensearch/security/DlsIntegrationTests.java +++ b/src/integrationTest/java/org/opensearch/security/DlsIntegrationTests.java @@ -10,12 +10,15 @@ package org.opensearch.security; import java.io.IOException; +import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.function.BiFunction; +import java.util.stream.Collectors; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.commons.lang3.tuple.Pair; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -36,6 +39,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.client.RequestOptions.DEFAULT; @@ -57,6 +61,7 @@ import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentsInAnyOrder; @RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) @ThreadLeakScope(ThreadLeakScope.Scope.NONE) @@ -82,6 +87,7 @@ public class DlsIntegrationTests { static final String FIRST_INDEX_ALIAS_FILTERED_BY_TWINS_ARTIST = FIRST_INDEX_NAME.concat("-filtered-by-twins-artist"); static final String FIRST_INDEX_ALIAS_FILTERED_BY_FIRST_ARTIST = FIRST_INDEX_NAME.concat("-filtered-by-first-artist"); static final String ALL_INDICES_ALIAS = "_all"; + static final String UNION_TEST_INDEX_NAME = "my_index1"; static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); @@ -158,6 +164,62 @@ public class DlsIntegrationTests { .on("*") ); + /** + * Test role 1 for DLS filtering with two (non)overlapping roles. This role imposes a filter where the user can only access documents where the sensitive field is false. This role is applied at a higher level for all index patterns. + */ + static final TestSecurityConfig.Role ROLE_NON_SENSITIVE_ONLY = new TestSecurityConfig.Role("test_role_1").clusterPermissions( + "cluster_composite_ops_ro" + ).indexPermissions("read").dls("{\"match\":{\"sensitive\":false}}").on("*"); + + /** + * Test role 2 for DLS filtering with two overlapping roles. This role does not impose any filter, and combined with TEST_ROLE_ONE should yield a union that does not impose any filter. This role is applied at a lower level for index patterns my_index*. + */ + static final TestSecurityConfig.Role ROLE_ALLOW_ALL = new TestSecurityConfig.Role("test_role_2").clusterPermissions( + "cluster_composite_ops_ro" + ).indexPermissions("read").dls("{\"match_all\": {}}").on("my_index*"); + + /** + * Test role 3 for DLS filtering with two nonoverlapping roles. This role imposes a filter where the user can only access documents where the genre field is History, and combined with TEST_ROLE_ONE should yield a union that allows the user to access every document except the one with genre Science and sensitive true. This role is applied at a lower level for index patterns my_index*. + */ + static final TestSecurityConfig.Role ROLE_MATCH_HISTORY_GENRE_ONLY = new TestSecurityConfig.Role("test_role_3").clusterPermissions( + "cluster_composite_ops_ro" + ).indexPermissions("read").dls("{\"match\":{\"genre\":\"History\"}}").on("my_index*"); + + /** + * User with DLS permission to only be able to access documents with false sensitive property. + */ + static final TestSecurityConfig.User USER_NON_SENSITIVE_ONLY = new TestSecurityConfig.User("test_role_1_user").roles( + ROLE_NON_SENSITIVE_ONLY + ); + + /** + * User with DLS permission to access all documents. + */ + static final TestSecurityConfig.User USER_ALLOW_ALL = new TestSecurityConfig.User("test_role_2_user").roles(ROLE_ALLOW_ALL); + + /** + * User with DLS permission to access documents with genre property matching History. + */ + static final TestSecurityConfig.User USER_MATCH_HISTORY_GENRE_ONLY = new TestSecurityConfig.User("test_role_3_user").roles( + ROLE_MATCH_HISTORY_GENRE_ONLY + ); + + /** + * User with overlapping DLS permissions to access documents with false sensitive property and access all documents- should yield accessing all documents. + */ + static final TestSecurityConfig.User USER_UNION_OF_OVERLAPPING_ROLES_NON_SENSITIVE_ONLY_AND_ALLOW_ALL = new TestSecurityConfig.User( + "test_union_of_overlapping_roles_user" + ).roles(ROLE_NON_SENSITIVE_ONLY, ROLE_ALLOW_ALL); + + /** + * User with non-overlapping DLS permissions to access documents with false sensitive property and genre property matching History. + */ + static final TestSecurityConfig.User USER_UNION_OF_NONOVERLAPPING_ROLES_NON_SENSITIVE_ONLY_AND_HISTORY_GENRE_ONLY = + new TestSecurityConfig.User("test_union_of_non_overlapping_roles_user").roles( + ROLE_NON_SENSITIVE_ONLY, + ROLE_MATCH_HISTORY_GENRE_ONLY + ); + @ClassRule public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) .anonymousAuth(false) @@ -172,7 +234,12 @@ public class DlsIntegrationTests { READ_WHERE_FIELD_ARTIST_MATCHES_ARTIST_STRING, READ_WHERE_STARS_LESS_THAN_THREE, READ_WHERE_FIELD_ARTIST_MATCHES_ARTIST_TWINS_OR_FIELD_STARS_GREATER_THAN_FIVE, - READ_WHERE_FIELD_ARTIST_MATCHES_ARTIST_TWINS_OR_MATCHES_ARTIST_FIRST + READ_WHERE_FIELD_ARTIST_MATCHES_ARTIST_TWINS_OR_MATCHES_ARTIST_FIRST, + USER_NON_SENSITIVE_ONLY, + USER_ALLOW_ALL, + USER_MATCH_HISTORY_GENRE_ONLY, + USER_UNION_OF_OVERLAPPING_ROLES_NON_SENSITIVE_ONLY_AND_ALLOW_ALL, + USER_UNION_OF_NONOVERLAPPING_ROLES_NON_SENSITIVE_ONLY_AND_HISTORY_GENRE_ONLY ) .build(); @@ -218,6 +285,21 @@ public class DlsIntegrationTests { } }; + static final TreeMap> UNION_ROLE_TEST_DATA = new TreeMap<>() { + { + put("1", Map.of("genre", "History", "date", "01-01-2020", "sensitive", true)); + put("2", Map.of("genre", "History", "date", "01-01-2020", "sensitive", true)); + put("3", Map.of("genre", "History", "date", "01-01-2020", "sensitive", true)); + put("4", Map.of("genre", "History", "date", "01-01-2020", "sensitive", true)); + put("5", Map.of("genre", "History", "date", "01-01-2020", "sensitive", true)); + put("6", Map.of("genre", "Math", "date", "01-01-2020", "sensitive", false)); + put("7", Map.of("genre", "Math", "date", "01-01-2020", "sensitive", false)); + put("8", Map.of("genre", "Math", "date", "01-01-2020", "sensitive", false)); + put("9", Map.of("genre", "Math", "date", "01-01-2020", "sensitive", false)); + put("10", Map.of("genre", "Science", "date", "01-01-2020", "sensitive", true)); + } + }; + @BeforeClass public static void createTestData() { try (Client client = cluster.getInternalNodeClient()) { @@ -275,6 +357,10 @@ public static void createTestData() { ) ) .actionGet(); + + UNION_ROLE_TEST_DATA.forEach((index, document) -> { + client.prepareIndex(UNION_TEST_INDEX_NAME).setId(index).setRefreshPolicy(IMMEDIATE).setSource(document).get(); + }); } } @@ -517,4 +603,121 @@ public void testAggregateAndComputeStarRatings() throws IOException { assertThat(((ParsedAvg) actualAggregation).getValue(), is(1.5)); } } + + @Test + public void testOverlappingRoleUnionSearchFiltering() throws Exception { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(USER_NON_SENSITIVE_ONLY)) { + SearchRequest searchRequest = new SearchRequest(UNION_TEST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertSearchResponseHitsEqualTo(searchResponse, 4); + + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + UNION_ROLE_TEST_DATA.entrySet() + .stream() + .filter(e -> e.getValue().get("sensitive").equals(false)) + .map(e -> Pair.of(UNION_TEST_INDEX_NAME, e.getKey())) + .collect(Collectors.toList()) + ) + ); + } + + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(USER_ALLOW_ALL)) { + SearchRequest searchRequest = new SearchRequest(UNION_TEST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertSearchResponseHitsEqualTo(searchResponse, 10); + } + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_UNION_OF_OVERLAPPING_ROLES_NON_SENSITIVE_ONLY_AND_ALLOW_ALL + ) + ) { + SearchRequest searchRequest = new SearchRequest(UNION_TEST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertSearchResponseHitsEqualTo(searchResponse, 10); + + // shows that roles are additive and the overlapping role with less filtering is used + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + UNION_ROLE_TEST_DATA.keySet().stream().map(id -> Pair.of(UNION_TEST_INDEX_NAME, id)).collect(Collectors.toList()) + ) + ); + } + } + + @Test + @SuppressWarnings("unchecked") + public void testNonOverlappingRoleUnionSearchFiltering() throws Exception { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(USER_NON_SENSITIVE_ONLY)) { + SearchRequest searchRequest = new SearchRequest(UNION_TEST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertSearchResponseHitsEqualTo(searchResponse, 4); + + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + UNION_ROLE_TEST_DATA.entrySet() + .stream() + .filter(e -> e.getValue().get("sensitive").equals(false)) + .map(e -> Pair.of(UNION_TEST_INDEX_NAME, e.getKey())) + .collect(Collectors.toList()) + ) + ); + } + + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(USER_MATCH_HISTORY_GENRE_ONLY)) { + SearchRequest searchRequest = new SearchRequest(UNION_TEST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertSearchResponseHitsEqualTo(searchResponse, 5); + + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + UNION_ROLE_TEST_DATA.entrySet() + .stream() + .filter(e -> e.getValue().get("genre").equals("History")) + .map(e -> Pair.of(UNION_TEST_INDEX_NAME, e.getKey())) + .collect(Collectors.toList()) + ) + ); + } + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_UNION_OF_NONOVERLAPPING_ROLES_NON_SENSITIVE_ONLY_AND_HISTORY_GENRE_ONLY + ) + ) { + SearchRequest searchRequest = new SearchRequest(UNION_TEST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertSearchResponseHitsEqualTo(searchResponse, 9); + + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + UNION_ROLE_TEST_DATA.keySet() + .stream() + .filter(id -> !id.equals("10")) + .map(id -> Pair.of(UNION_TEST_INDEX_NAME, id)) + .collect(Collectors.toList()) + ) + ); + + // shows that the roles are additive, but excludes one document since the DLS filters for both roles do not account for this + assertThat(searchResponse, not(searchHitsContainDocumentsInAnyOrder(Pair.of(UNION_TEST_INDEX_NAME, "10")))); + } + } + + private void assertSearchResponseHitsEqualTo(SearchResponse searchResponse, int hits) throws Exception { + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(hits)); + } }