Skip to content

Commit 60f27f3

Browse files
authored
Merge branch 'opensearch-project:main' into main
2 parents d706f15 + bac93dc commit 60f27f3

File tree

9 files changed

+200
-263
lines changed

9 files changed

+200
-263
lines changed

build.gradle

+8-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ buildscript {
2525
opensearch_build = version_tokens[0] + '.0'
2626

2727
common_utils_version = System.getProperty("common_utils.version", '3.0.0.0-SNAPSHOT')
28+
2829
kafka_version = '3.7.0'
2930
apache_cxf_version = '4.0.4'
3031
open_saml_version = '4.3.2'
@@ -471,7 +472,7 @@ configurations {
471472
resolutionStrategy {
472473
force 'commons-codec:commons-codec:1.17.0'
473474
force 'org.slf4j:slf4j-api:1.7.36'
474-
force 'org.scala-lang:scala-library:2.13.13'
475+
force 'org.scala-lang:scala-library:2.13.14'
475476
force "com.fasterxml.jackson:jackson-bom:${versions.jackson}"
476477
force "com.fasterxml.jackson.core:jackson-core:${versions.jackson}"
477478
force "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${versions.jackson}"
@@ -494,8 +495,8 @@ configurations {
494495
// For integrationTest
495496
force "org.apache.httpcomponents:httpclient:4.5.14"
496497
force "org.apache.httpcomponents:httpcore:4.4.16"
497-
force "com.google.errorprone:error_prone_annotations:2.27.0"
498-
force "org.checkerframework:checker-qual:3.42.0"
498+
force "com.google.errorprone:error_prone_annotations:2.27.1"
499+
force "org.checkerframework:checker-qual:3.43.0"
499500
force "ch.qos.logback:logback-classic:1.5.6"
500501
}
501502
}
@@ -605,12 +606,12 @@ dependencies {
605606
runtimeOnly 'com.eclipsesource.minimal-json:minimal-json:0.9.5'
606607
runtimeOnly 'commons-codec:commons-codec:1.17.0'
607608
runtimeOnly 'org.cryptacular:cryptacular:1.2.6'
608-
compileOnly 'com.google.errorprone:error_prone_annotations:2.27.0'
609+
compileOnly 'com.google.errorprone:error_prone_annotations:2.27.1'
609610
runtimeOnly 'com.sun.istack:istack-commons-runtime:4.2.0'
610611
runtimeOnly 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.2'
611612
runtimeOnly 'org.ow2.asm:asm:9.7'
612613

613-
testImplementation 'org.apache.camel:camel-xmlsecurity:3.22.1'
614+
testImplementation 'org.apache.camel:camel-xmlsecurity:3.22.2'
614615

615616
//OpenSAML
616617
implementation 'net.shibboleth.utilities:java-support:8.4.2'
@@ -649,7 +650,7 @@ dependencies {
649650
runtimeOnly 'org.apache.ws.xmlschema:xmlschema-core:2.3.1'
650651
runtimeOnly 'org.apache.santuario:xmlsec:2.3.4'
651652
runtimeOnly "com.github.luben:zstd-jni:${versions.zstd}"
652-
runtimeOnly 'org.checkerframework:checker-qual:3.42.0'
653+
runtimeOnly 'org.checkerframework:checker-qual:3.43.0'
653654
runtimeOnly "org.bouncycastle:bcpkix-jdk18on:${versions.bouncycastle}"
654655
runtimeOnly 'org.scala-lang.modules:scala-java8-compat_3:1.0.2'
655656

@@ -699,7 +700,7 @@ dependencies {
699700
testRuntimeOnly ("org.springframework:spring-core:${spring_version}") {
700701
exclude(group:'org.springframework', module: 'spring-jcl' )
701702
}
702-
testRuntimeOnly 'org.scala-lang:scala-library:2.13.13'
703+
testRuntimeOnly 'org.scala-lang:scala-library:2.13.14'
703704
testRuntimeOnly 'com.typesafe.scala-logging:scala-logging_3:3.9.5'
704705
testRuntimeOnly('org.apache.zookeeper:zookeeper:3.9.2') {
705706
exclude(group:'ch.qos.logback', module: 'logback-classic' )
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*
8+
* Modifications Copyright OpenSearch Contributors. See
9+
* GitHub history for details.
10+
*/
11+
package org.opensearch.security.api;
12+
13+
import java.util.StringJoiner;
14+
15+
import com.fasterxml.jackson.databind.node.ObjectNode;
16+
import org.junit.Test;
17+
18+
import org.opensearch.security.DefaultObjectMapper;
19+
import org.opensearch.security.dlic.rest.api.Endpoint;
20+
import org.opensearch.test.framework.cluster.TestRestClient;
21+
22+
import static org.opensearch.security.api.PatchPayloadHelper.patch;
23+
import static org.opensearch.security.api.PatchPayloadHelper.replaceOp;
24+
import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.SECURITY_CONFIG_UPDATE;
25+
import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ADMIN_ENABLED;
26+
import static org.opensearch.security.support.ConfigConstants.SECURITY_UNSUPPORTED_RESTAPI_ALLOW_SECURITYCONFIG_MODIFICATION;
27+
28+
public class ConfigRestApiIntegrationTest extends AbstractApiIntegrationTest {
29+
30+
final static String REST_API_ADMIN_CONFIG_UPDATE = "rest-api-admin-config-update";
31+
32+
static {
33+
clusterSettings.put(SECURITY_UNSUPPORTED_RESTAPI_ALLOW_SECURITYCONFIG_MODIFICATION, true).put(SECURITY_RESTAPI_ADMIN_ENABLED, true);
34+
testSecurityConfig.withRestAdminUser(REST_ADMIN_USER, allRestAdminPermissions())
35+
.withRestAdminUser(REST_API_ADMIN_CONFIG_UPDATE, restAdminPermission(Endpoint.CONFIG, SECURITY_CONFIG_UPDATE));
36+
}
37+
38+
private String securityConfigPath(final String... path) {
39+
final var fullPath = new StringJoiner("/").add(super.apiPath("securityconfig"));
40+
if (path != null) for (final var p : path)
41+
fullPath.add(p);
42+
return fullPath.toString();
43+
}
44+
45+
@Test
46+
public void forbiddenForRegularUsers() throws Exception {
47+
withUser(NEW_USER, client -> {
48+
forbidden(() -> client.get(securityConfigPath()));
49+
forbidden(() -> client.putJson(securityConfigPath("config"), EMPTY_BODY));
50+
forbidden(() -> client.patch(securityConfigPath(), EMPTY_BODY));
51+
verifyNotAllowedMethods(client);
52+
});
53+
}
54+
55+
@Test
56+
public void partiallyAvailableForAdminUser() throws Exception {
57+
withUser(ADMIN_USER_NAME, client -> ok(() -> client.get(securityConfigPath())));
58+
withUser(ADMIN_USER_NAME, client -> {
59+
badRequest(() -> client.putJson(securityConfigPath("xxx"), EMPTY_BODY));
60+
forbidden(() -> client.putJson(securityConfigPath("config"), EMPTY_BODY));
61+
forbidden(() -> client.patch(securityConfigPath(), EMPTY_BODY));
62+
});
63+
withUser(ADMIN_USER_NAME, this::verifyNotAllowedMethods);
64+
}
65+
66+
@Test
67+
public void availableForTlsAdminUser() throws Exception {
68+
withUser(ADMIN_USER_NAME, localCluster.getAdminCertificate(), client -> ok(() -> client.get(securityConfigPath())));
69+
withUser(ADMIN_USER_NAME, localCluster.getAdminCertificate(), this::verifyUpdate);
70+
}
71+
72+
@Test
73+
public void availableForRestAdminUser() throws Exception {
74+
withUser(REST_ADMIN_USER, client -> ok(() -> client.get(securityConfigPath())));
75+
withUser(REST_ADMIN_USER, this::verifyUpdate);
76+
withUser(REST_API_ADMIN_CONFIG_UPDATE, this::verifyUpdate);
77+
}
78+
79+
void verifyUpdate(final TestRestClient client) throws Exception {
80+
badRequest(() -> client.putJson(securityConfigPath("xxx"), EMPTY_BODY));
81+
verifyNotAllowedMethods(client);
82+
83+
final var configJson = ok(() -> client.get(securityConfigPath())).bodyAsJsonNode();
84+
final var authFailureListeners = DefaultObjectMapper.objectMapper.createObjectNode();
85+
authFailureListeners.set(
86+
"ip_rate_limiting",
87+
DefaultObjectMapper.objectMapper.createObjectNode()
88+
.put("type", "ip")
89+
.put("allowed_tries", 10)
90+
.put("time_window_seconds", 3_600)
91+
.put("block_expiry_seconds", 600)
92+
.put("max_blocked_clients", 100_000)
93+
.put("max_tracked_clients", 100_000)
94+
);
95+
authFailureListeners.set(
96+
"internal_authentication_backend_limiting",
97+
DefaultObjectMapper.objectMapper.createObjectNode()
98+
.put("type", "username")
99+
.put("authentication_backend", "intern")
100+
.put("allowed_tries", 10)
101+
.put("time_window_seconds", 3_600)
102+
.put("block_expiry_seconds", 600)
103+
.put("max_blocked_clients", 100_000)
104+
.put("max_tracked_clients", 100_000)
105+
);
106+
final var dynamicConfigJson = (ObjectNode) configJson.get("config").get("dynamic");
107+
dynamicConfigJson.set("auth_failure_listeners", authFailureListeners);
108+
ok(() -> client.putJson(securityConfigPath("config"), DefaultObjectMapper.writeValueAsString(configJson.get("config"), false)));
109+
ok(() -> client.patch(securityConfigPath(), patch(replaceOp("/config/dynamic/hosts_resolver_mode", "other"))));
110+
}
111+
112+
void verifyNotAllowedMethods(final TestRestClient client) throws Exception {
113+
methodNotAllowed(() -> client.postJson(securityConfigPath(), EMPTY_BODY));
114+
methodNotAllowed(() -> client.delete(securityConfigPath()));
115+
}
116+
117+
}

src/integrationTest/java/org/opensearch/security/api/DefaultApiAvailabilityIntegrationTest.java

+3-12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import static org.hamcrest.MatcherAssert.assertThat;
1919
import static org.hamcrest.core.Is.is;
20+
import static org.opensearch.security.api.PatchPayloadHelper.patch;
21+
import static org.opensearch.security.api.PatchPayloadHelper.replaceOp;
2022

2123
public class DefaultApiAvailabilityIntegrationTest extends AbstractApiIntegrationTest {
2224

@@ -49,18 +51,7 @@ private void verifySecurityConfigApi(final TestRestClient client) throws Excepti
4951
methodNotAllowed(() -> client.putJson(apiPath("securityconfig"), EMPTY_BODY));
5052
methodNotAllowed(() -> client.postJson(apiPath("securityconfig"), EMPTY_BODY));
5153
methodNotAllowed(() -> client.delete(apiPath("securityconfig")));
52-
forbidden(
53-
() -> client.patch(
54-
apiPath("securityconfig"),
55-
(builder, params) -> builder.startArray()
56-
.startObject()
57-
.field("op", "replace")
58-
.field("path", "/a/b/c")
59-
.field("value", "other")
60-
.endObject()
61-
.endArray()
62-
)
63-
);
54+
forbidden(() -> client.patch(apiPath("securityconfig"), patch(replaceOp("/a/b/c", "other"))));
6455
}
6556

6657
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*
8+
* Modifications Copyright OpenSearch Contributors. See
9+
* GitHub history for details.
10+
*/
11+
12+
package org.opensearch.security.api;
13+
14+
import java.util.Locale;
15+
16+
import org.opensearch.core.xcontent.ToXContentObject;
17+
18+
interface PatchPayloadHelper extends ToXContentObject {
19+
20+
enum Op {
21+
ADD,
22+
REPLACE,
23+
REMOVE;
24+
}
25+
26+
static <T> ToXContentObject addOp(final String path, final T value) {
27+
return operation(Op.ADD, path, value);
28+
}
29+
30+
static <T> ToXContentObject replaceOp(final String path, final T value) {
31+
return operation(Op.REPLACE, path, value);
32+
}
33+
34+
static ToXContentObject removeOp(final String path) {
35+
return operation(Op.REMOVE, path, null);
36+
}
37+
38+
private static <T> ToXContentObject operation(final Op op, final String path, final T value) {
39+
return (builder, params) -> {
40+
final var opPath = path.startsWith("/") ? path : "/" + path;
41+
builder.startObject().field("op", op.name().toLowerCase(Locale.ROOT)).field("path", opPath);
42+
if (value != null) {
43+
if (value instanceof ToXContentObject) {
44+
builder.field("value", (ToXContentObject) value);
45+
} else if (value instanceof String) {
46+
builder.field("value", (String) value);
47+
} else if (value instanceof Boolean) {
48+
builder.field("value", (Boolean) value);
49+
} else {
50+
throw new IllegalArgumentException("Unsupported java type " + value.getClass());
51+
}
52+
}
53+
return builder.endObject();
54+
};
55+
}
56+
57+
static ToXContentObject patch(final ToXContentObject... operations) {
58+
return (builder, params) -> {
59+
builder.startArray();
60+
for (final var o : operations)
61+
o.toXContent(builder, EMPTY_PARAMS);
62+
return builder.endArray();
63+
};
64+
}
65+
66+
}

src/integrationTest/java/org/opensearch/test/framework/cluster/LocalOpenSearchCluster.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ public class LocalOpenSearchCluster {
105105

106106
private File snapshotDir;
107107

108-
private int nodeCounter = 0;
109-
110108
public LocalOpenSearchCluster(
111109
String clusterName,
112110
ClusterManager clusterManager,
@@ -302,10 +300,9 @@ private CompletableFuture<Void> startNodes(
302300
Iterator<Integer> httpPortIterator = httpPorts.iterator();
303301
List<CompletableFuture<StartStage>> futures = new ArrayList<>();
304302

305-
for (NodeSettings nodeSettings : nodeSettingList) {
306-
Node node = new Node(nodeCounter, nodeSettings, transportPortIterator.next(), httpPortIterator.next());
303+
for (var i = 0; i < nodeSettingList.size(); i++) {
304+
Node node = new Node(i, nodeSettingList.get(i), transportPortIterator.next(), httpPortIterator.next());
307305
futures.add(node.start());
308-
nodeCounter += 1;
309306
}
310307
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
311308
}
@@ -518,7 +515,6 @@ private Settings getOpenSearchSettings() {
518515
.build();
519516

520517
if (nodeSettingsSupplier != null) {
521-
// TODO node number
522518
return Settings.builder().put(settings).put(nodeSettingsSupplier.get(nodeNumber)).build();
523519
}
524520
return settings;

src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import org.opensearch.cluster.metadata.IndexAbstraction;
8383
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
8484
import org.opensearch.cluster.service.ClusterService;
85+
import org.opensearch.common.util.IndexUtils;
8586
import org.opensearch.core.index.Index;
8687
import org.opensearch.index.IndexNotFoundException;
8788
import org.opensearch.index.reindex.ReindexRequest;
@@ -91,7 +92,6 @@
9192
import org.opensearch.security.support.SnapshotRestoreHelper;
9293
import org.opensearch.security.support.WildcardMatcher;
9394
import org.opensearch.snapshots.SnapshotInfo;
94-
import org.opensearch.snapshots.SnapshotUtils;
9595
import org.opensearch.transport.RemoteClusterService;
9696
import org.opensearch.transport.TransportRequest;
9797

@@ -694,7 +694,7 @@ private boolean getOrReplaceAllIndices(final Object request, final IndicesProvid
694694
);
695695
provider.provide(new String[] { "*" }, request, false);
696696
} else {
697-
final List<String> requestedResolvedIndices = SnapshotUtils.filterIndices(
697+
final List<String> requestedResolvedIndices = IndexUtils.filterIndices(
698698
snapshotInfo.indices(),
699699
restoreRequest.indices(),
700700
restoreRequest.indicesOptions()

src/main/java/org/opensearch/security/support/SnapshotRestoreHelper.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@
3737
import org.opensearch.SpecialPermission;
3838
import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
3939
import org.opensearch.action.support.PlainActionFuture;
40+
import org.opensearch.common.util.IndexUtils;
4041
import org.opensearch.repositories.RepositoriesService;
4142
import org.opensearch.repositories.Repository;
4243
import org.opensearch.security.OpenSearchSecurityPlugin;
4344
import org.opensearch.snapshots.SnapshotId;
4445
import org.opensearch.snapshots.SnapshotInfo;
45-
import org.opensearch.snapshots.SnapshotUtils;
4646
import org.opensearch.threadpool.ThreadPool;
4747

4848
public class SnapshotRestoreHelper {
@@ -56,7 +56,7 @@ public static List<String> resolveOriginalIndices(RestoreSnapshotRequest restore
5656
log.warn("snapshot repository '{}', snapshot '{}' not found", restoreRequest.repository(), restoreRequest.snapshot());
5757
return null;
5858
} else {
59-
return SnapshotUtils.filterIndices(snapshotInfo.indices(), restoreRequest.indices(), restoreRequest.indicesOptions());
59+
return IndexUtils.filterIndices(snapshotInfo.indices(), restoreRequest.indices(), restoreRequest.indicesOptions());
6060
}
6161

6262
}

0 commit comments

Comments
 (0)