-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: cliu123 <[email protected]>
- Loading branch information
1 parent
c70e91e
commit 1e1ddbd
Showing
5 changed files
with
339 additions
and
0 deletions.
There are no files selected for viewing
227 changes: 227 additions & 0 deletions
227
src/test/java/org/opensearch/security/dlic/dlsfls/DfmOverwritesAllTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file 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.opensearch.security.dlic.dlsfls; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.opensearch.action.index.IndexRequest; | ||
import org.opensearch.action.support.WriteRequest.RefreshPolicy; | ||
import org.opensearch.client.Client; | ||
import org.opensearch.common.settings.Settings; | ||
import org.opensearch.common.xcontent.XContentType; | ||
import org.opensearch.security.support.ConfigConstants; | ||
import org.opensearch.security.test.DynamicSecurityConfig; | ||
import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; | ||
|
||
/** | ||
* Tests that the dfm_empty_overwrites_all flag works correctly. | ||
* Per default, if a user has a role that adds restrictions to an index | ||
* regarding DLS, FLS or masked fields (DFM), and another role that has no restrictions | ||
* on the same index, the restrictions from the first role still applies. | ||
* | ||
* If the dfm_empty_overwrites_all flag is set to true, the logic is reversed: | ||
* If a user has a role that places no restrictions on an index, this trumps | ||
* all other role that eventually do place restrictions on this index. | ||
*/ | ||
public class DfmOverwritesAllTest extends AbstractDlsFlsTest { | ||
|
||
protected void populateData(Client tc) { | ||
|
||
tc.index(new IndexRequest("index1-1").id("0").setRefreshPolicy(RefreshPolicy.IMMEDIATE) | ||
.source("{\"field1\": 1, \"field2\": \"value-2-1\", \"field3\": \"value-3-1\", \"field4\": \"value-4-1\" }", XContentType.JSON)).actionGet(); | ||
|
||
tc.index(new IndexRequest("index1-2").id("0").setRefreshPolicy(RefreshPolicy.IMMEDIATE) | ||
.source("{\"field1\": 2, \"field2\": \"value-2-2\", \"field3\": \"value-3-2\", \"field4\": \"value-4-2\" }", XContentType.JSON)).actionGet(); | ||
|
||
tc.index(new IndexRequest("index1-3").id("0").setRefreshPolicy(RefreshPolicy.IMMEDIATE) | ||
.source("{\"field1\": 3, \"field2\": \"value-2-3\", \"field3\": \"value-3-3\", \"field4\": \"value-4-3\" }", XContentType.JSON)).actionGet(); | ||
|
||
tc.index(new IndexRequest("index1-4").id("0").setRefreshPolicy(RefreshPolicy.IMMEDIATE) | ||
.source("{\"field1\": 4, \"field2\": \"value-2-4\", \"field3\": \"value-3-4\", \"field4\": \"value-4-4\" }", XContentType.JSON)).actionGet(); | ||
|
||
} | ||
|
||
@Test | ||
public void testDFMUnrestrictedUser() throws Exception { | ||
// admin user sees all, no dfm restrictions apply | ||
final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, true).build(); | ||
|
||
setup(settings, new DynamicSecurityConfig().setConfig("securityconfig_dfm_empty_overwrites_all.yml") | ||
.setSecurityInternalUsers("internal_users_dfm_empty_overwrites_all.yml") | ||
.setSecurityRoles("roles_dfm_empty_overwrites_all.yml") | ||
.setSecurityRolesMapping("rolesmapping_dfm_empty_overwrites_all.yml")); | ||
|
||
HttpResponse response; | ||
|
||
response = rh.executeGetRequest("/index1-*/_search?pretty", | ||
encodeBasicHeader("admin", "password")); | ||
Assert.assertEquals(200, response.getStatusCode()); | ||
|
||
// the only document in index1-1 is filtered by DLS query, so normally no hit in index-1-1 | ||
Assert.assertTrue(response.getBody().contains("index1-1")); | ||
|
||
// field3 and field4 - normally filtered out by FLS | ||
Assert.assertTrue(response.getBody().contains("value-3-1")); | ||
Assert.assertTrue(response.getBody().contains("value-4-1")); | ||
Assert.assertTrue(response.getBody().contains("value-3-2")); | ||
Assert.assertTrue(response.getBody().contains("value-4-2")); | ||
Assert.assertTrue(response.getBody().contains("value-3-3")); | ||
Assert.assertTrue(response.getBody().contains("value-4-3")); | ||
Assert.assertTrue(response.getBody().contains("value-3-4")); | ||
Assert.assertTrue(response.getBody().contains("value-4-4")); | ||
|
||
// field2 - normally masked | ||
Assert.assertTrue(response.getBody().contains("value-2-1")); | ||
Assert.assertTrue(response.getBody().contains("value-2-2")); | ||
Assert.assertTrue(response.getBody().contains("value-2-3")); | ||
Assert.assertTrue(response.getBody().contains("value-2-4")); | ||
} | ||
|
||
|
||
@Test | ||
public void testDFMRestrictedUser() throws Exception { | ||
// tests that the DFM settings are applied. User has only one role | ||
// with D/F/M all enabled, so restrictions must kick in | ||
final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, true).build(); | ||
|
||
setup(settings, new DynamicSecurityConfig().setConfig("securityconfig_dfm_empty_overwrites_all.yml") | ||
.setSecurityInternalUsers("internal_users_dfm_empty_overwrites_all.yml") | ||
.setSecurityRoles("roles_dfm_empty_overwrites_all.yml") | ||
.setSecurityRolesMapping("rolesmapping_dfm_empty_overwrites_all.yml")); | ||
|
||
HttpResponse response; | ||
|
||
response = rh.executeGetRequest("/index1-*/_search?pretty", | ||
encodeBasicHeader("dfm_restricted_role", "password")); | ||
Assert.assertEquals(200, response.getStatusCode()); | ||
|
||
// the only document in index1-1 is filtered by DLS query, so no hit in index-1-1 | ||
Assert.assertFalse(response.getBody().contains("index1-1")); | ||
|
||
// field3 and field4 - filtered out by FLS | ||
Assert.assertFalse(response.getBody().contains("value-3-1")); | ||
Assert.assertFalse(response.getBody().contains("value-4-1")); | ||
Assert.assertFalse(response.getBody().contains("value-3-2")); | ||
Assert.assertFalse(response.getBody().contains("value-4-2")); | ||
Assert.assertFalse(response.getBody().contains("value-3-3")); | ||
Assert.assertFalse(response.getBody().contains("value-4-3")); | ||
Assert.assertFalse(response.getBody().contains("value-3-4")); | ||
Assert.assertFalse(response.getBody().contains("value-4-4")); | ||
|
||
// field2 - masked | ||
Assert.assertFalse(response.getBody().contains("value-2-1")); | ||
Assert.assertFalse(response.getBody().contains("value-2-2")); | ||
Assert.assertFalse(response.getBody().contains("value-2-3")); | ||
Assert.assertFalse(response.getBody().contains("value-2-4")); | ||
|
||
// field2 - check also some masked vallues | ||
Assert.assertTrue(response.getBody().contains("514b27191e2322b0f7cd6afc3a5d657ff438fd0cc8dc229bd1a589804fdffd99")); | ||
Assert.assertTrue(response.getBody().contains("3090f7e867f390fb96b20ba30ee518b09a927b857393ebd1262f31191a385efa")); | ||
} | ||
|
||
@Test | ||
public void testDFMRestrictedAndUnrestrictedAllIndices() throws Exception { | ||
|
||
// user has the restricted role as in test testDFMRestrictedUser(). In addition, user has | ||
// another role with the same index pattern as the restricted role but no DFM settings. In that | ||
// case the unrestricted role should trump the restricted one, so basically user has | ||
// full access again. | ||
final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, true).build(); | ||
|
||
setup(settings, new DynamicSecurityConfig().setConfig("securityconfig_dfm_empty_overwrites_all.yml") | ||
.setSecurityInternalUsers("internal_users_dfm_empty_overwrites_all.yml") | ||
.setSecurityRoles("roles_dfm_empty_overwrites_all.yml") | ||
.setSecurityRolesMapping("rolesmapping_dfm_empty_overwrites_all.yml")); | ||
|
||
HttpResponse response; | ||
|
||
response = rh.executeGetRequest("/index1-*/_search?pretty", | ||
encodeBasicHeader("dfm_restricted_and_unrestricted_all_indices_role", "password")); | ||
Assert.assertEquals(200, response.getStatusCode()); | ||
|
||
// the only document in index1-1 is filtered by DLS query, so normally no hit in index-1-1 | ||
Assert.assertTrue(response.getBody().contains("index1-1")); | ||
|
||
// field3 and field4 - normally filtered out by FLS | ||
Assert.assertTrue(response.getBody().contains("value-3-1")); | ||
Assert.assertTrue(response.getBody().contains("value-4-1")); | ||
Assert.assertTrue(response.getBody().contains("value-3-2")); | ||
Assert.assertTrue(response.getBody().contains("value-4-2")); | ||
Assert.assertTrue(response.getBody().contains("value-3-3")); | ||
Assert.assertTrue(response.getBody().contains("value-4-3")); | ||
Assert.assertTrue(response.getBody().contains("value-3-4")); | ||
Assert.assertTrue(response.getBody().contains("value-4-4")); | ||
|
||
// field2 - normally masked | ||
Assert.assertTrue(response.getBody().contains("value-2-1")); | ||
Assert.assertTrue(response.getBody().contains("value-2-2")); | ||
Assert.assertTrue(response.getBody().contains("value-2-3")); | ||
Assert.assertTrue(response.getBody().contains("value-2-4")); | ||
} | ||
|
||
@Test | ||
public void testDFMRestrictedAndUnrestrictedOneIndex() throws Exception { | ||
|
||
// user has the restricted role as in test testDFMRestrictedUser(). In addition, user has | ||
// another role where the index pattern matches two specific index ("index1-2", "index-1-1"), means this role has two indices | ||
// which are more specific than the index pattern in the restricted role ("index1-*"), So the second role should | ||
// remove the DMF restrictions from exactly two indices. Otherwise, restrictions still apply. | ||
final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, true).build(); | ||
setup(settings, new DynamicSecurityConfig().setConfig("securityconfig_dfm_empty_overwrites_all.yml") | ||
.setSecurityInternalUsers("internal_users_dfm_empty_overwrites_all.yml") | ||
.setSecurityRoles("roles_dfm_empty_overwrites_all.yml") | ||
.setSecurityRolesMapping("rolesmapping_dfm_empty_overwrites_all.yml")); | ||
|
||
HttpResponse response; | ||
|
||
response = rh.executeGetRequest("/_plugins/_security/authinfo?pretty", | ||
encodeBasicHeader("dfm_restricted_and_unrestricted_one_index_role", "password")); | ||
|
||
response = rh.executeGetRequest("/index1-*/_search?pretty", | ||
encodeBasicHeader("dfm_restricted_and_unrestricted_one_index_role", "password")); | ||
Assert.assertEquals(200, response.getStatusCode()); | ||
|
||
// we have a role that places no restrictions on index-1-1, lifting the DLS from the restricted role | ||
Assert.assertTrue(response.getBody().contains("index1-1")); | ||
Assert.assertTrue(response.getBody().contains("value-2-1")); | ||
Assert.assertTrue(response.getBody().contains("value-3-1")); | ||
Assert.assertTrue(response.getBody().contains("value-4-1")); | ||
|
||
// field3 and field4 - normally filtered out by FLS. The second role | ||
// lifts restrictions for index1-1 and index1-4, so only those | ||
// values should be visible for index1-1 and index1-4 | ||
Assert.assertTrue(response.getBody().contains("value-3-1")); | ||
Assert.assertTrue(response.getBody().contains("value-4-1")); | ||
Assert.assertTrue(response.getBody().contains("value-3-4")); | ||
Assert.assertTrue(response.getBody().contains("value-4-4")); | ||
|
||
// FLS restrictions still in place for index1-2 and index1-3, those | ||
// fields must not be present | ||
Assert.assertFalse(response.getBody().contains("value-3-2")); | ||
Assert.assertFalse(response.getBody().contains("value-4-2")); | ||
Assert.assertFalse(response.getBody().contains("value-3-3")); | ||
Assert.assertFalse(response.getBody().contains("value-4-3")); | ||
|
||
// field2 - normally masked, but for index1-1 and index1-4 restrictions are | ||
// lifted by second role, so we have cleartext in index1-1 and index1-4 | ||
Assert.assertTrue(response.getBody().contains("value-2-1")); | ||
Assert.assertTrue(response.getBody().contains("value-2-4")); | ||
|
||
// but we still have masked values for index1-2 and index1-3 | ||
Assert.assertTrue(response.getBody().contains("514b27191e2322b0f7cd6afc3a5d657ff438fd0cc8dc229bd1a589804fdffd99")); | ||
Assert.assertTrue(response.getBody().contains("3090f7e867f390fb96b20ba30ee518b09a927b857393ebd1262f31191a385efa")); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/test/resources/dlsfls/internal_users_dfm_empty_overwrites_all.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
--- | ||
_meta: | ||
type: "internalusers" | ||
config_version: 2 | ||
|
||
admin: | ||
#password | ||
hash: $2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe | ||
|
||
dfm_user: | ||
#password | ||
hash: $2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe | ||
|
||
dfm_restricted_and_unrestricted_all_indices_role: | ||
#password | ||
hash: $2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe | ||
|
||
dfm_restricted_and_unrestricted_one_index_role: | ||
#password | ||
hash: $2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe | ||
|
||
dfm_restricted_role: | ||
#password | ||
hash: $2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe |
50 changes: 50 additions & 0 deletions
50
src/test/resources/dlsfls/roles_dfm_empty_overwrites_all.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
--- | ||
_meta: | ||
type: "roles" | ||
config_version: 2 | ||
|
||
os_admin: | ||
cluster_permissions: | ||
- '*' | ||
index_permissions: | ||
- index_patterns: | ||
- '*' | ||
allowed_actions: | ||
- '*' | ||
|
||
os_dfm_restricted_all_indices: | ||
cluster_permissions: | ||
- "*" | ||
index_permissions: | ||
- index_patterns: | ||
- "index1-*" | ||
allowed_actions: | ||
- "*" | ||
dls: '{ "bool": { "must_not": { "term": { "field1": 1 }}}}' | ||
masked_fields: | ||
- "field2" | ||
fls: | ||
- "~field3" | ||
- "~field4" | ||
|
||
os_dfm_not_restricted_all_indices: | ||
cluster_permissions: | ||
- "*" | ||
index_permissions: | ||
- index_patterns: | ||
- "index1-*" | ||
allowed_actions: | ||
- "*" | ||
|
||
os_dfm_not_restricted_one_index: | ||
cluster_permissions: | ||
- "*" | ||
index_permissions: | ||
- index_patterns: | ||
- "index1-4" | ||
allowed_actions: | ||
- "*" | ||
- index_patterns: | ||
- "index1-1" | ||
allowed_actions: | ||
- "*" |
22 changes: 22 additions & 0 deletions
22
src/test/resources/dlsfls/rolesmapping_dfm_empty_overwrites_all.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
_meta: | ||
type: "rolesmapping" | ||
config_version: 2 | ||
|
||
os_admin: | ||
users: | ||
- admin | ||
|
||
os_dfm_restricted_all_indices: | ||
users: | ||
- dfm_restricted_role | ||
- dfm_restricted_and_unrestricted_one_index_role | ||
- dfm_restricted_and_unrestricted_all_indices_role | ||
|
||
os_dfm_not_restricted_all_indices: | ||
users: | ||
- dfm_restricted_and_unrestricted_all_indices_role | ||
|
||
os_dfm_not_restricted_one_index: | ||
users: | ||
- dfm_restricted_and_unrestricted_one_index_role |
16 changes: 16 additions & 0 deletions
16
src/test/resources/dlsfls/securityconfig_dfm_empty_overwrites_all.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
_meta: | ||
type: "config" | ||
config_version: 2 | ||
config: | ||
dynamic: | ||
do_not_fail_on_forbidden: true | ||
authc: | ||
authentication_domain_basic_internal: | ||
http_enabled: true | ||
transport_enabled: true | ||
order: 0 | ||
http_authenticator: | ||
type: "basic" | ||
authentication_backend: | ||
type: "intern" |