Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,11 @@ allprojects {
integrationTestImplementation 'org.slf4j:slf4j-api:2.0.12'
integrationTestImplementation 'com.selectivem.collections:special-collections-complete:1.4.0'
integrationTestImplementation "org.opensearch.plugin:lang-painless:${opensearch_version}"

integrationTestImplementation ('com.jayway.jsonpath:json-path:2.9.0') {
exclude(group: 'net.minidev', module: 'json-smart')
}
integrationTestImplementation 'net.minidev:json-smart:2.6.0'
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import org.opensearch.action.search.SearchRequest;
import org.opensearch.action.search.SearchResponse;
import org.opensearch.action.search.SearchScrollRequest;
import org.opensearch.action.support.IndicesOptions;
import org.opensearch.action.update.UpdateRequest;
import org.opensearch.action.update.UpdateResponse;
import org.opensearch.client.RestHighLevelClient;
Expand Down Expand Up @@ -2281,7 +2282,11 @@ public void openIndex_negative() throws IOException {
.open(new OpenIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT),
statusException(FORBIDDEN)
);
assertThatThrownBy(() -> restHighLevelClient.indices().open(new OpenIndexRequest("*"), DEFAULT), statusException(FORBIDDEN));
assertThatThrownBy(
() -> restHighLevelClient.indices()
.open(new OpenIndexRequest("*").indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED), DEFAULT),
statusException(FORBIDDEN)
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,12 @@
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;

import org.opensearch.cluster.ClusterState;
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.security.resolver.IndexResolverReplacer;
import org.opensearch.security.support.WildcardMatcher;
import org.opensearch.security.user.User;
import org.opensearch.security.util.MockPrivilegeEvaluationContextBuilder;

import static org.opensearch.security.util.MockIndexMetadataBuilder.indices;
import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -232,19 +226,10 @@ public void equals() {
}

private static PrivilegesEvaluationContext ctx() {
IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY));
IndexResolverReplacer indexResolverReplacer = new IndexResolverReplacer(indexNameExpressionResolver, () -> CLUSTER_STATE, null);
User user = new User("test_user").withAttributes(ImmutableMap.of("attrs.a11", "a11", "attrs.year", "year"));
return new PrivilegesEvaluationContext(
user,
ImmutableSet.of(),
"indices:action/test",
null,
null,
indexResolverReplacer,
indexNameExpressionResolver,
() -> CLUSTER_STATE,
ActionPrivileges.EMPTY
);
return MockPrivilegeEvaluationContextBuilder.ctx()
.action("indices:action/test")
.attr("attrs.a11", "a11")
.attr("attrs.year", "year")
.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.security.privileges;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Suite;

import org.opensearch.action.ActionRequest;
import org.opensearch.action.IndicesRequest;
import org.opensearch.action.OriginalIndices;
import org.opensearch.action.index.IndexRequest;
import org.opensearch.action.search.SearchRequest;
import org.opensearch.action.support.IndicesOptions;
import org.opensearch.cluster.ClusterState;
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.cluster.metadata.ResolvedIndices;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.security.util.MockIndexMetadataBuilder;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

@RunWith(Suite.class)
@Suite.SuiteClasses({ IndexRequestModifierTest.SetLocalIndices.class, IndexRequestModifierTest.SetLocalIndicesToEmpty.class })
public class IndexRequestModifierTest {

static final IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(
new ThreadContext(Settings.EMPTY)
);
static final Metadata metadata = MockIndexMetadataBuilder.indices("index", "index1", "index2", "index3").build();
final static ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).metadata(metadata).build();
static final IndicesRequestModifier subject = new IndicesRequestModifier();

public static class SetLocalIndices {
@Test
public void basic() {
ResolvedIndices resolvedIndices = ResolvedIndices.of("index1");
SearchRequest request = new SearchRequest("index1", "index2", "index3");

boolean success = subject.setLocalIndices(request, resolvedIndices, Collections.singletonList("index1"));
assertTrue(success);
assertArrayEquals(new String[] { "index1" }, request.indices());
}

@Test
public void withRemote() {
ResolvedIndices resolvedIndices = ResolvedIndices.of("index1")
.withRemoteIndices(
Map.of("remote", new OriginalIndices(new String[] { "index_remote" }, IndicesOptions.LENIENT_EXPAND_OPEN))
);
SearchRequest request = new SearchRequest("index1", "index2", "index3", "remote:index_remote");

boolean success = subject.setLocalIndices(request, resolvedIndices, Collections.singletonList("index1"));
assertTrue(success);
assertArrayEquals(new String[] { "index1", "remote:index_remote" }, request.indices());
}

@Test
public void empty() {
ResolvedIndices resolvedIndices = ResolvedIndices.of("index1");
SearchRequest request = new SearchRequest("index1", "index2", "index3");

boolean success = subject.setLocalIndices(request, resolvedIndices, Collections.emptyList());
assertTrue(success);
String[] finalResolvedIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, request);
assertArrayEquals(new String[0], finalResolvedIndices);
}

@Test
public void unsupportedType() {
ResolvedIndices resolvedIndices = ResolvedIndices.of("index1");
IndexRequest request = new IndexRequest("index1");

boolean success = subject.setLocalIndices(request, resolvedIndices, Collections.singletonList("index1"));
assertFalse(success);
}
}

@RunWith(Parameterized.class)
public static class SetLocalIndicesToEmpty {

String description;
IndicesRequest request;

@Test
public void setLocalIndicesToEmpty() {

ResolvedIndices resolvedIndices = ResolvedIndices.of("index");

if (Arrays.asList(request.indices()).contains("remote:index")) {
resolvedIndices = resolvedIndices.withRemoteIndices(
Map.of("remote", new OriginalIndices(new String[] { "index" }, request.indicesOptions()))
);
}

boolean success = subject.setLocalIndicesToEmpty((ActionRequest) request, resolvedIndices);

if (!(request instanceof IndicesRequest.Replaceable)) {
assertFalse(success);
} else if (!request.indicesOptions().allowNoIndices()) {
assertFalse(success);
} else {
assertTrue(success);

String[] finalResolvedIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, request);

assertEquals("Resolved to empty indices: " + Arrays.asList(finalResolvedIndices), 0, finalResolvedIndices.length);
}
}

@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> params() {
return Arrays.asList(
new Object[] { "lenient expand open", new SearchRequest("index").indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN) },
new Object[] {
"lenient expand open/closed",
new SearchRequest("index").indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED) },
new Object[] {
"lenient expand open/closed/hidden",
new SearchRequest("index").indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN) },
new Object[] {
"allow no indices",
new SearchRequest("index").indicesOptions(IndicesOptions.fromOptions(false, true, false, false)) },
new Object[] {
"ignore unavailable",
new SearchRequest("index").indicesOptions(IndicesOptions.fromOptions(true, false, false, false)) },
new Object[] {
"strict single index",
new SearchRequest("index").indicesOptions(IndicesOptions.STRICT_SINGLE_INDEX_NO_EXPAND_FORBID_CLOSED) },
new Object[] {
"with remote index",
new SearchRequest("index", "remote:index").indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN) },
new Object[] { "not implementing IndicesRequest.Replaceable", new IndexRequest("index") }
);

}

public SetLocalIndicesToEmpty(String description, IndicesRequest request) {
this.description = description;
this.request = request;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.security.privileges;

import org.opensearch.cluster.ClusterState;
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.security.util.MockIndexMetadataBuilder;

public class IndicesRequestResolverTest {

static final Metadata metadata = MockIndexMetadataBuilder.indices("index1", "index2", "index3").build();
final static ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).metadata(metadata).build();
static final IndicesRequestResolver subject = new IndicesRequestResolver(
new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY))
);
/*
@Test
public void resolve_normal() {
SearchRequest request = new SearchRequest("index1");
ActionRequestMetadata<SearchRequest, ?> actionRequestMetadata = mock();
ResolvedIndices resolvedIndices = ResolvedIndices.of("index1");
when(actionRequestMetadata.resolvedIndices()).thenReturn(Optional.of(resolvedIndices));

ResolvedIndices returnedResolvedIndices = subject.resolve(request, actionRequestMetadata, () -> clusterState);
assertEquals(resolvedIndices, returnedResolvedIndices);
}

@Test
public void resolve_fallback() {
SearchRequest request = new SearchRequest("index1");
ActionRequestMetadata<SearchRequest, ?> actionRequestMetadata = mock();
when(actionRequestMetadata.resolvedIndices()).thenReturn(Optional.empty());

ResolvedIndices returnedResolvedIndices = subject.resolve(request, actionRequestMetadata, () -> clusterState);
assertEquals(Set.of("index1"), returnedResolvedIndices.local().names());
}

@Test
public void resolve_fallbackUnsupported() {
ClusterStatsRequest request = new ClusterStatsRequest();
ActionRequestMetadata<SearchRequest, ?> actionRequestMetadata = mock();
when(actionRequestMetadata.resolvedIndices()).thenReturn(Optional.empty());

ResolvedIndices returnedResolvedIndices = subject.resolve(request, actionRequestMetadata, () -> clusterState);
assertTrue("Expected isAll(), got: " + returnedResolvedIndices, returnedResolvedIndices.local().isAll());
}

@Test
public void resolve_withPrivilegesEvaluationContext() {
SearchRequest request = new SearchRequest("index*");
ActionRequestMetadata<SearchRequest, ?> actionRequestMetadata = mock();
when(actionRequestMetadata.resolvedIndices()).thenReturn(Optional.empty());
PrivilegesEvaluationContext context = MockPrivilegeEvaluationContextBuilder.ctx().clusterState(clusterState).get();

ResolvedIndices returnedResolvedIndices = subject.resolve(request, actionRequestMetadata, context);
assertEquals(Set.of("index1", "index2", "index3"), returnedResolvedIndices.local().names());
}*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.opensearch.security.dlic.rest.api.Endpoint;
import org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.PermissionBuilder;
import org.opensearch.security.privileges.actionlevel.RoleBasedActionPrivileges;
import org.opensearch.security.privileges.actionlevel.RuntimeOptimizedActionPrivileges;
import org.opensearch.security.securityconf.FlattenedActionGroups;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration;
Expand Down Expand Up @@ -117,7 +118,13 @@ static String[] allRestApiPermissions() {
final RoleBasedActionPrivileges actionPrivileges;

public RestEndpointPermissionTests() throws IOException {
this.actionPrivileges = new RoleBasedActionPrivileges(createRolesConfig(), FlattenedActionGroups.EMPTY, Settings.EMPTY);
this.actionPrivileges = new RoleBasedActionPrivileges(
createRolesConfig(),
FlattenedActionGroups.EMPTY,
RuntimeOptimizedActionPrivileges.SpecialIndexProtection.NONE,
Settings.EMPTY,
false
);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

import org.junit.Test;

import org.opensearch.cluster.metadata.ResolvedIndices;
import org.opensearch.security.privileges.ActionPrivileges;
import org.opensearch.security.privileges.PrivilegesEvaluatorResponse;
import org.opensearch.security.resolver.IndexResolverReplacer;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.opensearch.security.privileges.PrivilegeEvaluatorResponseMatcher.isForbidden;
Expand Down Expand Up @@ -50,7 +50,7 @@ public void hasIndexPrivilege() {
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(
ctx().get(),
Set.of("indices:data/write/index"),
IndexResolverReplacer.Resolved.ofIndex("any_index")
ResolvedIndices.of("any_index")
);
assertThat(result, isForbidden());
}
Expand All @@ -60,7 +60,7 @@ public void hasExplicitIndexPrivilege() {
PrivilegesEvaluatorResponse result = subject.hasExplicitIndexPrivilege(
ctx().get(),
Set.of("indices:data/write/index"),
IndexResolverReplacer.Resolved.ofIndex("any_index")
ResolvedIndices.of("any_index")
);
assertThat(result, isForbidden());
}
Expand Down
Loading