Skip to content

Commit a7d2c57

Browse files
authored
[Backport 2.x] Implement new extension points in IdentityPlugin and add ContextProvidingPluginSubject (opensearch-project#5028)
Signed-off-by: Craig Perkins <[email protected]>
1 parent 4af1d07 commit a7d2c57

32 files changed

+1321
-38
lines changed

.github/actions/run-bwc-suite/action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ runs:
5050
-Dbwc.version.previous=${{ steps.build-previous.outputs.built-version }}
5151
-Dbwc.version.next=${{ steps.build-next.outputs.built-version }} -i
5252
53-
- uses: actions/upload-artifact@v3
53+
- uses: actions/upload-artifact@v4
5454
if: always()
5555
with:
5656
name: ${{ inputs.report-artifact-name }}

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ jobs:
118118
arguments: |
119119
integrationTest -Dbuild.snapshot=false
120120
121-
- uses: alehechka/upload-tartifact@v2
121+
- uses: actions/upload-artifact@v4
122122
if: always()
123123
with:
124124
name: integration-${{ matrix.platform }}-JDK${{ matrix.jdk }}-reports

.github/workflows/integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424

2525
- run: OPENDISTRO_SECURITY_TEST_OPENSSL_OPT=true ./gradlew test
2626

27-
- uses: actions/upload-artifact@v3
27+
- uses: actions/upload-artifact@v4
2828
if: always()
2929
with:
3030
name: ${{ matrix.jdk }}-${{ matrix.test-run }}-reports

src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,28 @@ public void wildcard() throws Exception {
126126
);
127127
}
128128

129+
@Test
130+
public void wildcardByUsername() throws Exception {
131+
SecurityDynamicConfiguration<RoleV7> roles = SecurityDynamicConfiguration.empty(CType.ROLES);
132+
133+
ActionPrivileges subject = new ActionPrivileges(
134+
roles,
135+
FlattenedActionGroups.EMPTY,
136+
null,
137+
Settings.EMPTY,
138+
Map.of("plugin:org.opensearch.sample.SamplePlugin", Set.of("*"))
139+
);
140+
141+
assertThat(
142+
subject.hasClusterPrivilege(ctxByUsername("plugin:org.opensearch.sample.SamplePlugin"), "cluster:whatever"),
143+
isAllowed()
144+
);
145+
assertThat(
146+
subject.hasClusterPrivilege(ctx("plugin:org.opensearch.other.OtherPlugin"), "cluster:whatever"),
147+
isForbidden(missingPrivileges("cluster:whatever"))
148+
);
149+
}
150+
129151
@Test
130152
public void explicit_wellKnown() throws Exception {
131153
SecurityDynamicConfiguration<RoleV7> roles = SecurityDynamicConfiguration.fromYaml("non_explicit_role:\n" + //
@@ -455,7 +477,8 @@ public IndicesAndAliases(IndexSpec indexSpec, ActionSpec actionSpec, Statefulnes
455477
settings,
456478
WellKnownActions.CLUSTER_ACTIONS,
457479
WellKnownActions.INDEX_ACTIONS,
458-
WellKnownActions.INDEX_ACTIONS
480+
WellKnownActions.INDEX_ACTIONS,
481+
Map.of()
459482
);
460483

461484
if (statefulness == Statefulness.STATEFUL || statefulness == Statefulness.STATEFUL_LIMITED) {
@@ -1030,4 +1053,19 @@ static PrivilegesEvaluationContext ctx(String... roles) {
10301053
null
10311054
);
10321055
}
1056+
1057+
static PrivilegesEvaluationContext ctxByUsername(String username) {
1058+
User user = new User(username);
1059+
user.addAttributes(ImmutableMap.of("attrs.dept_no", "a11"));
1060+
return new PrivilegesEvaluationContext(
1061+
user,
1062+
ImmutableSet.of(),
1063+
null,
1064+
null,
1065+
null,
1066+
null,
1067+
new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)),
1068+
null
1069+
);
1070+
}
10331071
}

src/integrationTest/java/org/opensearch/security/SystemIndexTests.java renamed to src/integrationTest/java/org/opensearch/security/systemindex/SystemIndexTests.java

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* compatible open source license.
88
*
99
*/
10-
package org.opensearch.security;
10+
package org.opensearch.security.systemindex;
1111

1212
import java.util.List;
1313
import java.util.Map;
@@ -19,17 +19,22 @@
1919
import org.junit.runner.RunWith;
2020

2121
import org.opensearch.core.rest.RestStatus;
22-
import org.opensearch.security.http.ExampleSystemIndexPlugin;
22+
import org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1;
23+
import org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin2;
2324
import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain;
2425
import org.opensearch.test.framework.cluster.ClusterManager;
2526
import org.opensearch.test.framework.cluster.LocalCluster;
2627
import org.opensearch.test.framework.cluster.TestRestClient;
2728
import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse;
29+
import org.opensearch.test.framework.matcher.RestMatchers;
2830

2931
import static org.hamcrest.MatcherAssert.assertThat;
32+
import static org.hamcrest.Matchers.containsString;
3033
import static org.hamcrest.Matchers.equalTo;
3134
import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED;
3235
import static org.opensearch.security.support.ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY;
36+
import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1.SYSTEM_INDEX_1;
37+
import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin2.SYSTEM_INDEX_2;
3338
import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS;
3439
import static org.opensearch.test.framework.TestSecurityConfig.User.USER_ADMIN;
3540

@@ -44,7 +49,7 @@ public class SystemIndexTests {
4449
.anonymousAuth(false)
4550
.authc(AUTHC_DOMAIN)
4651
.users(USER_ADMIN)
47-
.plugin(ExampleSystemIndexPlugin.class)
52+
.plugin(SystemIndexPlugin1.class, SystemIndexPlugin2.class)
4853
.nodeSettings(
4954
Map.of(
5055
SECURITY_RESTAPI_ROLES_ENABLED,
@@ -89,6 +94,100 @@ public void adminShouldNotBeAbleToDeleteSecurityIndex() {
8994
}
9095
}
9196

97+
@Test
98+
public void testPluginShouldBeAbleToIndexDocumentIntoItsSystemIndex() {
99+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
100+
HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_1);
101+
102+
assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus()));
103+
assertThat(response.getBody(), containsString("{\"acknowledged\":true}"));
104+
}
105+
}
106+
107+
@Test
108+
public void testPluginShouldNotBeAbleToIndexDocumentIntoSystemIndexRegisteredByOtherPlugin() {
109+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
110+
HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_2);
111+
112+
assertThat(
113+
response,
114+
RestMatchers.isForbidden(
115+
"/error/root_cause/0/reason",
116+
"no permissions for [] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1"
117+
)
118+
);
119+
}
120+
}
121+
122+
@Test
123+
public void testPluginShouldBeAbleToCreateSystemIndexButUserShouldNotBeAbleToIndex() {
124+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
125+
HttpResponse response = client.put("try-create-and-index/" + SYSTEM_INDEX_1 + "?runAs=user");
126+
127+
assertThat(response, RestMatchers.isForbidden("/error/root_cause/0/reason", "no permissions for [] and User [name=admin"));
128+
}
129+
}
130+
131+
@Test
132+
public void testPluginShouldNotBeAbleToRunClusterActions() {
133+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
134+
HttpResponse response = client.get("try-cluster-health/plugin");
135+
136+
assertThat(
137+
response,
138+
RestMatchers.isForbidden(
139+
"/error/root_cause/0/reason",
140+
"no permissions for [cluster:monitor/health] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1"
141+
)
142+
);
143+
}
144+
}
145+
146+
@Test
147+
public void testAdminUserShouldBeAbleToRunClusterActions() {
148+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
149+
HttpResponse response = client.get("try-cluster-health/user");
150+
151+
assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus()));
152+
}
153+
}
154+
155+
@Test
156+
public void testAuthenticatedUserShouldBeAbleToRunClusterActions() {
157+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
158+
HttpResponse response = client.get("try-cluster-health/default");
159+
160+
assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus()));
161+
}
162+
}
163+
164+
@Test
165+
public void testPluginShouldBeAbleToBulkIndexDocumentIntoItsSystemIndex() {
166+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
167+
HttpResponse response = client.put("try-create-and-bulk-index/" + SYSTEM_INDEX_1);
168+
169+
assertThat(response.getStatusCode(), equalTo(RestStatus.OK.getStatus()));
170+
}
171+
}
172+
173+
@Test
174+
public void testPluginShouldNotBeAbleToBulkIndexDocumentIntoMixOfSystemIndexWhereAtLeastOneDoesNotBelongToPlugin() {
175+
try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) {
176+
client.put(".system-index1");
177+
client.put(".system-index2");
178+
}
179+
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
180+
HttpResponse response = client.put("try-create-and-bulk-mixed-index");
181+
182+
assertThat(
183+
response.getBody(),
184+
containsString(
185+
"no permissions for [] and User [name=plugin:org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1"
186+
)
187+
);
188+
}
189+
}
190+
92191
@Test
93192
public void regularUserShouldGetNoResultsWhenSearchingSystemIndex() {
94193
// Create system index and index a dummy document as the super admin user, data returned to super admin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*
9+
*/
10+
11+
package org.opensearch.security.systemindex.sampleplugin;
12+
13+
// CS-SUPPRESS-SINGLE: RegexpSingleline It is not possible to use phrase "cluster manager" instead of master here
14+
import org.opensearch.action.ActionType;
15+
import org.opensearch.action.support.master.AcknowledgedResponse;
16+
// CS-ENFORCE-SINGLE
17+
18+
public class IndexDocumentIntoSystemIndexAction extends ActionType<AcknowledgedResponse> {
19+
public static final IndexDocumentIntoSystemIndexAction INSTANCE = new IndexDocumentIntoSystemIndexAction();
20+
public static final String NAME = "cluster:mock/systemindex/index";
21+
22+
private IndexDocumentIntoSystemIndexAction() {
23+
super(NAME, AcknowledgedResponse::new);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*
9+
*/
10+
11+
package org.opensearch.security.systemindex.sampleplugin;
12+
13+
import java.io.IOException;
14+
15+
import org.opensearch.action.ActionRequest;
16+
import org.opensearch.action.ActionRequestValidationException;
17+
import org.opensearch.core.common.io.stream.StreamInput;
18+
19+
public class IndexDocumentIntoSystemIndexRequest extends ActionRequest {
20+
21+
private final String indexName;
22+
23+
private final String runAs;
24+
25+
public IndexDocumentIntoSystemIndexRequest(String indexName, String runAs) {
26+
this.indexName = indexName;
27+
this.runAs = runAs;
28+
}
29+
30+
public IndexDocumentIntoSystemIndexRequest(StreamInput in) throws IOException {
31+
super(in);
32+
this.indexName = in.readString();
33+
this.runAs = in.readOptionalString();
34+
}
35+
36+
@Override
37+
public ActionRequestValidationException validate() {
38+
return null;
39+
}
40+
41+
public String getIndexName() {
42+
return this.indexName;
43+
}
44+
45+
public String getRunAs() {
46+
return this.runAs;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*
9+
*/
10+
11+
package org.opensearch.security.systemindex.sampleplugin;
12+
13+
import java.util.List;
14+
15+
import org.opensearch.action.bulk.BulkRequest;
16+
import org.opensearch.action.bulk.BulkRequestBuilder;
17+
import org.opensearch.action.index.IndexRequest;
18+
import org.opensearch.action.support.WriteRequest;
19+
import org.opensearch.client.Client;
20+
import org.opensearch.client.node.NodeClient;
21+
import org.opensearch.core.action.ActionListener;
22+
import org.opensearch.core.rest.RestStatus;
23+
import org.opensearch.core.xcontent.ToXContent;
24+
import org.opensearch.rest.BaseRestHandler;
25+
import org.opensearch.rest.BytesRestResponse;
26+
import org.opensearch.rest.RestChannel;
27+
import org.opensearch.rest.RestRequest;
28+
29+
import static java.util.Collections.singletonList;
30+
import static org.opensearch.rest.RestRequest.Method.PUT;
31+
import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin1.SYSTEM_INDEX_1;
32+
import static org.opensearch.security.systemindex.sampleplugin.SystemIndexPlugin2.SYSTEM_INDEX_2;
33+
34+
public class RestBulkIndexDocumentIntoMixOfSystemIndexAction extends BaseRestHandler {
35+
36+
private final Client client;
37+
private final RunAsSubjectClient pluginClient;
38+
39+
public RestBulkIndexDocumentIntoMixOfSystemIndexAction(Client client, RunAsSubjectClient pluginClient) {
40+
this.client = client;
41+
this.pluginClient = pluginClient;
42+
}
43+
44+
@Override
45+
public List<Route> routes() {
46+
return singletonList(new Route(PUT, "/try-create-and-bulk-mixed-index"));
47+
}
48+
49+
@Override
50+
public String getName() {
51+
return "test_bulk_index_document_into_mix_of_system_index_action";
52+
}
53+
54+
@Override
55+
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) {
56+
return new RestChannelConsumer() {
57+
58+
@Override
59+
public void accept(RestChannel channel) throws Exception {
60+
BulkRequestBuilder builder = client.prepareBulk();
61+
builder.add(new IndexRequest(SYSTEM_INDEX_1).source("content", 1));
62+
builder.add(new IndexRequest(SYSTEM_INDEX_2).source("content", 1));
63+
builder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
64+
BulkRequest bulkRequest = builder.request();
65+
pluginClient.bulk(bulkRequest, ActionListener.wrap(r -> {
66+
channel.sendResponse(new BytesRestResponse(RestStatus.OK, r.toXContent(channel.newBuilder(), ToXContent.EMPTY_PARAMS)));
67+
}, fr -> { channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, String.valueOf(fr))); }));
68+
}
69+
};
70+
}
71+
}

0 commit comments

Comments
 (0)